我介意着你的不介意。

Android 重构 | 持续优化统一管理 Gradle 「未完待续」_第1张图片

前言

借着韩哥哥要求重构的机会,正好好好回顾下以前遗忘/忽略的知识点。

记录下有关 Gradle 优化之路:

  • Android 重构 | 统一管理 Gradle 依赖版本
  • Android | 模块化探索抽取 basic 简化子 module 冗余

大概的方向或者说最终目标精简后如下:

  • 一次引用,全文(项目)使用,避免团队协作引入重复依赖;
  • 自带依赖更新提示;
  • 支持跳转等常规操作。

最重要的,依然是便于维护。

从最初的创建 config.gradle 到现在的 basic_depend.gradle,虽说今天更比昨天强,但是依然不是很满意。

ext 方式虽然是 Google 官方目前推荐,并且当前一些主流库也采用此种方式,实际使用起来,个人还是有部分不方便。比如说不支持跳转,不支持更新等等,人呐,总想得到更多。

在查阅了多个文档后,再次准备优化/升级一波,继续让韩总蒙圈。

一、buildSrc 搞起来

将官方的描述用 Google 翻译了一边,如下:

复杂的构建逻辑通常很适合作为自定义任务或二进制插件进行封装。自定义任务和插件实现不应存在于构建脚本中。buildSrc 只要不需要在多个独立项目之间共享代码,就可以非常方便地使用该代码。

该目录 buildSrc 被视为包含的构建。发现目录后,Gradle 会自动编译并测试此代码,并将其放入构建脚本的类路径中。对于多项目构建,只能有一个 buildSrc 目录,该目录必须位于根项目目录中。 buildSrc 应该比脚本插件更可取,因为它更易于维护,重构和测试代码。

buildSrc 使用适用于 Java 和 Groovy 项目的相同源代码约定。它还提供对 Gradle API 的直接访问。其他依赖项可以在专用的 build.gradle 下声明 buildSrc。

思索许久,个人简单总结下:

  • buildSrc 存在于 Gradle 编译期;
  • 同样 buildSrc 支持(单独项目)共享代码,例如一个项目中多个 module 都可以直接调用。

buildSrc 实践

描述下操作步骤:

  • 在项目根目录下创建 buildSrc 目录,随后新建 build.gradle.kts 文件;
  • 创建 src 目录,以及对应管理版本文件;
  • 替换直接使用原有依赖

build.gradle.kts 内容如下:

// 导入 Kotlin 插件import org.gradle.kotlin.dsl.`kotlin-dsl`plugins {    `kotlin-dsl`}repositories {    jcenter()}/** * 禁用测试报告(Gradle 默认会自动创建测试报告) */tasks.withType {    reports.html.isEnabled = false    reports.junitXml.isEnabled = false}/** *  isFork:将编译器作为单独的进程运行。 *  该过程在构建期间将被重用,因此分叉开销很小。分叉的好处是,内存密集型编译是在不同的过程中进行的,从而导致主 Gradle 守护程序中的垃圾回收量大大减少。 *  守护程序中较少的垃圾收集意味着 Gradle 的基础架构可以运行得更快,尤其是在您还使用的情况下 --parallel。 * *  isIncremental:增量编译。Gradle 可以分析直至单个类级别的依赖关系,以便仅重新编译受更改影响的类。自 Gradle 4.10 起,增量编译是默认设置。 */tasks.withType {    options.isFork = true    options.isIncremental = true}/** * 禁用关于使用实验性 Kotlin 编译器功能的警告 */kotlinDslPluginOptions {    experimentalWarning.set(false)}

Dependencies.kt,这是我定义的版本管理的文件,部分内容如下:

@file:Suppress("SpellCheckingInspection")/** * @author HLQ_Struggle * @date 2020/7/27 * @desc 统一管理类 */// 统一管理项目中的版本信息object Versions {    // Build Config    const val compileSDK = 29       // 编译 SDK 版本    const val buildTools = "29.0.3" // Gradle 编译项目工具版本    const val minSDK = 23           // 最低兼容 Android 版本    const val targetSDK = 29        // 最高兼容 Android 版本    // App Version    const val appVersionCode = 1           // 当前版本编号    const val appVersionName = "1.0"       // 当前版本信息    // Plugins    const val androidGradlePlugin = "4.0.1"    // Kotlin    const val kotlin = "1.3.72"    const val kotlinxCoroutines = "1.3.5"    // Support Lib    const val support = "29.0.3"    const val appcompat = "1.1.0"    const val constrainLayout = "1.1.3"    // Testing    const val junit = "4.12"    const val extJunit = "1.1.1"    const val espresso = "3.2.0"}// 统一管理项目中使用的依赖库object Deps {    // Gradle    const val androidGradle = "com.android.tools.build:gradle:${Versions.androidGradlePlugin}"    // Kotlin    const val kotlinStdLib = "org.jetbrains.kotlin:kotlin-stdlib-jdk7:${Versions.kotlin}"    const val kotlinGradlePlugin = "org.jetbrains.kotlin:kotlin-gradle-plugin:${Versions.kotlin}"    const val kotlinxCoroutines =        "org.jetbrains.kotlinx:kotlinx-coroutines-android:${Versions.kotlinxCoroutines}"    // Testing    const val junit = "junit:junit:${Versions.junit}"    const val extJunit = "androidx.test.ext:junit:${Versions.extJunit}"    const val espresso = "androidx.test.espresso:espresso-core:${Versions.espresso}"    // Android    const val appcompat = "androidx.appcompat:appcompat:${Versions.appcompat}"    const val coreKtx = "androidx.core:core-ktx:1.2.0"    const val constraintLayout =        "androidx.constraintlayout:constraintlayout:${Versions.constrainLayout}"    // Fix:关于 64K 引用限制    const val multiDex = "androidx.multidex:multidex:2.0.1"    // Jetpack    const val viewPager2 = "androidx.viewpager2:viewpager2:1.0.0"    // ...}

举个两个栗子,如何使用:

  • 根目录下 build 如何使用:

直接通过在 Dependencies 文件中定义的分组名去获取对应的属性即可,如下所示:

buildscript {    // ...    dependencies {        classpath Deps.androidGradle        classpath Deps.kotlinGradlePlugin        // NOTE: Do not place your application dependencies here; they belong        // in the individual module build.gradle files    }}// ...
  • 其它 module 目录下 build 如何使用:

同理,当然也可以采用直接倒入整个对应分组方式,直接使用对应属性,例如:

// 这里采用直接倒入定义的 Deps 以及 Versions 分组方式import static Deps.*import static Versions.*apply plugin: 'com.android.library'apply plugin: 'kotlin-android'apply plugin: 'kotlin-android-extensions'android {    // 这里就可以直接使用对应的属性    compileSdkVersion compileSDK    buildToolsVersion buildTools    defaultConfig {        minSdkVersion minSDK        targetSdkVersion targetSDK        versionCode appVersionCode        versionName appVersionName        testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner"        consumerProguardFiles 'consumer-rules.pro'    }    // ...}dependencies {    implementation fileTree(dir: 'libs', include: ['*.jar'])    // 同理,这里也是一样,直接使用对应的属性名即可    implementation kotlinStdLib    implementation appcompat    implementation coreKtx    api 'com.google.android.material:material:1.2.0'    testImplementation junit    androidTestImplementation extJunit    androidTestImplementation espresso    api mmkv    api 'com.airbnb.android:lottie:3.4.1'}

这种方式比较有好的几个特点如下:

  • 支持跳转;
  • 支持智能提示;
  • Gradle 编译时介入,感脚很湿高大上

但是关键的更新提示呢?

ummm,不开森。

加个 gif 配图吧~

Android 重构 | 持续优化统一管理 Gradle 「未完待续」_第2张图片

手动编写 buildSrc 需要注意:

  • 目录结构:例如:buildSrc/src/main/kotlin(java)
  • 在 build.gradle.kts 中添加 jcenter(),否则 kotlin-dsl 加载失败

二、refreshVersions 使用(2020/09/15)

网上搜到关于 refreshVersions 的描述,觉得蛮合适,尝试一波。

大概的优势在于以下几点:

  • 集中管理依赖
  • 以最小成本提示依赖升级

操作步骤如下:

Step 1:修改 settings.gradle 文件

// settings.gradle.ktsimport de.fayard.refreshVersions.RefreshVersionsSetup// Here you might have some pluginManagement block:pluginManagement {    //...}buildscript {    repositories { gradlePluginPortal() }    dependencies.classpath("de.fayard.refreshVersions:refreshVersions:0.9.5")}rootProject.name = 'Your Android Project Name'include ':app'include ':helper'include ':weight'// include other moduleRefreshVersionsSetup.bootstrap(settings)

Step 2:同步后执行命令

./gradlew migrateToRefreshVersionsDependenciesConstants --console=plain

根据提示进行依赖替换:

Android 重构 | 持续优化统一管理 Gradle 「未完待续」_第3张图片

随后生成 versions.properties 文件:

## suppress inspection "SpellCheckingInspection" for whole file## suppress inspection "UnusedProperty" for whole file#### Dependencies and Plugin versions with their available updates## Generated by $ ./gradlew refreshVersions## Please, don't put extra comments in that file yet, keeping them is not supported yet.version.androidx.appcompat=1.1.0##             # available=1.2.0-alpha01##             # available=1.2.0-alpha02##             # available=1.2.0-alpha03##             # available=1.2.0-beta01##             # available=1.2.0-rc01##             # available=1.2.0-rc02##             # available=1.2.0##             # available=1.3.0-alpha01##             # available=1.3.0-alpha02version.androidx.core=1.2.0##        # available=1.3.0-alpha01##        # available=1.3.0-alpha02##        # available=1.3.0-beta01##        # available=1.3.0-rc01##        # available=1.3.0##        # available=1.3.1##        # available=1.4.0-alpha01##        # available=1.5.0-alpha01##        # available=1.5.0-alpha02## 。。。

有一点觉得不舒服的地方是,它内置了 Android 一部分的依赖,而对于我们实际开发中使用其它依赖,则显示不太友好了,如下图:

Android 重构 | 持续优化统一管理 Gradle 「未完待续」_第4张图片

研究好一段时间,各种蒙圈,实际的效果还是不是太满意,如果能在 buildSrc 的基础上新增版本更新就更好了。

三、未知

期待能和 buildSrc 结合使用。

希望有所了解大佬指点一二。

参考资料

  • 配置项目全局属性
  • Use buildSrc to abstract imperative logic
  • refreshVersions

更多相关文章

  1. Android 应用的版本兼容 了解一下(理解 minSdkVersion、targetSdk
  2. Android App内版本更新完美适配7.0、8.0
  3. android sdk 兼容低版本的处理方法
  4. Android SELinux开发入门指南之正确姿势解决访问data目录权限问
  5. Android 导入v7包常见错误,以及项目引用v7包错误解决
  6. Android代码混淆及项目发布方法记录

随机推荐

  1. Android屏幕密度(Density)和分辨率的关系
  2. Android(安卓)Training - 创建一个Androi
  3. Google手机操作系统Android将100%开源
  4. 修改AVD的存放位置
  5. 第一行代码Android第一课
  6. Android(安卓)root权限获取大揭秘
  7. 1.5.20 Android(安卓)Adapter 用法总结
  8. Android(安卓)HAL层分析
  9. Android(安卓)下载的三种实现方式(文件流
  10. 使用GoAgent更新Android(安卓)SDK