|
| 1 | +Composing builds简介 |
| 2 | +=== |
| 3 | + |
| 4 | +在Android Studio项目中,经常会引用多个Module,而且会有多人同时参与项目开发,在这种背景下,会时常遇到版本冲突问题,出现不同的compileSdkVersion或者出现同一个库的多个不同版本等,导致我们的包体变大,项目运行时间变长,所以将依赖版本统一是一个项目优化的必经之路。 |
| 5 | + |
| 6 | +到目前为止Google为管理Gradle依赖提供了4种不同方法: |
| 7 | + |
| 8 | +- 手动管理 |
| 9 | + |
| 10 | + 在每个module中定义插件依赖库,每次升级依赖库时都需要手动更改(不建议使用)。 |
| 11 | + |
| 12 | +- ext的方式管理 |
| 13 | + |
| 14 | + 这是Google推荐管理依赖的方法 [Android官方文档](https://developer.android.com/studio/build/gradle-tips#configure-project-wide-properties)。但是无法跟踪依赖关系,可读性差,不便维护。 |
| 15 | + |
| 16 | +- Kotlin + buildSrc |
| 17 | + |
| 18 | + 支持自动补全和单击跳转,依赖更新时将重新构建整个项目。 |
| 19 | + |
| 20 | +- Composing builds |
| 21 | + |
| 22 | + 自动补全和单击跳转,依赖更新时不会重新构建整个项目。 |
| 23 | + |
| 24 | + |
| 25 | + |
| 26 | +## Groovy ext扩展函数的替代方式 |
| 27 | + |
| 28 | +我们在使用Groovy语言构建项目的时候,抽取config.gradle作为全局的变量控制,使用ext扩展函数来统一配置依赖,如下: |
| 29 | + |
| 30 | + |
| 31 | + |
| 32 | +```groovy |
| 33 | +ext { |
| 34 | + android = [ |
| 35 | + compileSdkVersion: 29, |
| 36 | + buildToolsVersion: "29", |
| 37 | + minSdkVersion : 17, |
| 38 | + targetSdkVersion : 26, |
| 39 | + versionCode : 102, |
| 40 | + versionName : "1.0.2" |
| 41 | + ] |
| 42 | +
|
| 43 | + version = [ |
| 44 | + appcompatVersion : "1.1.0", |
| 45 | + coreKtxVersion : "1.2.0", |
| 46 | + supportLibraryVersion : "28.0.0", |
| 47 | + androidTestVersion : "3.0.1", |
| 48 | + junitVersion : "4.12", |
| 49 | + glideVersion : "4.11.0", |
| 50 | + okhttpVersion : "3.11.0", |
| 51 | + retrofitVersion : "2.3.0", |
| 52 | + constraintLayoutVersion: "1.1.3", |
| 53 | + gsonVersion : "2.7", |
| 54 | + rxjavaVersion : "2.2.2", |
| 55 | + rxandroidVersion : "2.1.0", |
| 56 | + ..........省略........... |
| 57 | + ] |
| 58 | +
|
| 59 | + dependencies = [ |
| 60 | + //base |
| 61 | + "constraintLayout" : "androidx.constraintlayout:constraintlayout:${version["constraintLayoutVersion"]}", |
| 62 | + "appcompat" : "androidx.appcompat:appcompat:${version["appcompatVersion"]}", |
| 63 | + "coreKtx" : "androidx.core:core-ktx:${version["coreKtxVersion"]}", |
| 64 | + "material" : "com.google.android.material:material:1.2.1", |
| 65 | +
|
| 66 | + //multidex |
| 67 | + "multidex" : "com.android.support:multidex:${version["multidexVersion"]}", |
| 68 | +
|
| 69 | + //okhttp |
| 70 | + "okhttp" : "com.squareup.okhttp3:okhttp:${version["okhttpVersion"]}", |
| 71 | + "logging-interceptor" : "com.squareup.okhttp3:logging-interceptor:${version["okhttpVersion"]}", |
| 72 | +
|
| 73 | + //retrofit |
| 74 | + "retrofit" : "com.squareup.retrofit2:retrofit:${version["retrofitVersion"]}", |
| 75 | + "converter-gson" : "com.squareup.retrofit2:converter-gson:${version["retrofitVersion"]}", |
| 76 | + "adapter-rxjava2" : "com.squareup.retrofit2:adapter-rxjava2:${version["retrofitVersion"]}", |
| 77 | + "converter-scalars" : "com.squareup.retrofit2:converter-scalars:${version["retrofitVersion"]}", |
| 78 | + ..........省略........... |
| 79 | + ] |
| 80 | +} |
| 81 | +``` |
| 82 | + |
| 83 | +依赖写完之后,在root路径下的build.gradle添加以下代码: |
| 84 | + |
| 85 | +```groovy |
| 86 | +apply from: "config.gradle" |
| 87 | +``` |
| 88 | + |
| 89 | +然后在需要依赖的module下的build.gradle中: |
| 90 | + |
| 91 | +```groovy |
| 92 | +dependencies { |
| 93 | + ... |
| 94 | + // Retrofit + okhttp 相关的依赖包 |
| 95 | + api rootProject.ext.dependencies["retrofit"] |
| 96 | + ... |
| 97 | +} |
| 98 | +``` |
| 99 | + |
| 100 | +以上就是Groovy ext扩展函数的依赖管理方式,此方式可以做到版本依赖,但是最大的缺点就是无法跟踪代码,想要找到上面示例代码中的rootProject.ext.dependencies["retrofit"]这个依赖,需要手动切到config.gradle去搜索查找,可读性很差。 |
| 101 | + |
| 102 | + |
| 103 | + |
| 104 | +## buildSrc+kotlin |
| 105 | + |
| 106 | +Android Gradle插件4.0支持在Gradle构建配置中使用Kotlin脚本 (KTS),用于替代Groovy(过去在Gradle配置文件中使用的编程语言)。 |
| 107 | + |
| 108 | +将来,KTS会比Groovy更适合用于编写Gradle脚本,因为采用Kotlin编写的代码可读性更高,并且Kotlin提供了更好的编译时检查和IDE支持。 |
| 109 | + |
| 110 | +虽然与Groovy相比,KTS当前能更好地在Android Studio的代码编辑器中集成,但采用KTS的构建速度往往比采用Groovy慢,因此在迁移到KTS时应考虑构建性能。 |
| 111 | + |
| 112 | +KTS:是指Kotlin脚本,这是Gradle在构建配置文件中使用的一种[Kotlin语言形式](https://kotlinlang.org/docs/tutorials/command-line.html#run-scripts)。Kotlin脚本是[可从命令行运行](https://kotlinlang.org/docs/tutorials/command-line.html#using-the-command-line-to-run-scripts)的Kotlin代码。 |
| 113 | + |
| 114 | +Kotlin DSL:主要是指[Android Gradle插件Kotlin DSL](https://developer.android.com/reference/tools/gradle-api),有时也指[底层Gradle Kotlin DSL](https://guides.gradle.org/migrating-build-logic-from-groovy-to-kotlin/)。 |
| 115 | + |
| 116 | + |
| 117 | + |
| 118 | +摘自 [Gradle 文档](https://docs.gradle.org/current/userguide/organizing_gradle_projects.html#sec:build_sources):当运行Gradle时会检查根项目中是否存在一个名为buildSrc的目录,该目录包含了项目build相关的逻辑。然后Gradle会自动编译并测试这段代码,并将其放入构建脚本的类路径中, 对于多项目构建,只能有一个buildSrc目录,该目录必须位于根项目目录中,buildSrc是Gradle项目根目录下的一个目录,它可以包含我们的构建逻辑,与脚本插件相比,buildSrc应该是首选,因为它更易于维护、重构和测试代码。 |
| 119 | + |
| 120 | +buildSrc的方式,是最近几年特别流行的版本依赖管理方式。它有以下几个优点: |
| 121 | + |
| 122 | +- 支持双向跟踪 |
| 123 | +- buildSrc是Android默认插件,全局只有这一个地方可以修改 |
| 124 | +- 支持Android Studio的代码补全 |
| 125 | + |
| 126 | +使用方式可参考:[Kotlin + buildSrc for Better Gradle Dependency Management](https://handstandsam.com/2018/02/11/kotlin-buildsrc-for-better-gradle-dependency-management/) |
| 127 | + |
| 128 | +- 在项目根目录下新建一个名为buildSrc的文件夹( 名字必须是buildSrc,因为运行Gradle时会检查项目中是否存在一个名为buildSrc的目录 ) |
| 129 | +- 在buildSrc文件夹里创建名为build.gradle.kts的文件,添加以下内容: |
| 130 | + |
| 131 | +```kotlin |
| 132 | +plugins { |
| 133 | + `kotlin-dsl` |
| 134 | +} |
| 135 | +repositories{ |
| 136 | + jcenter |
| 137 | +} |
| 138 | +``` |
| 139 | + |
| 140 | +- 在buildSrc/src/main/java/包名/目录下新建Deps.kt文件,添加以下内容: |
| 141 | + |
| 142 | +```groovy |
| 143 | +object Versions { |
| 144 | + val appcompat = "1.1.0" |
| 145 | +} |
| 146 | +
|
| 147 | +object Deps { |
| 148 | + val appcompat = "androidx.appcompat:appcompat:${Versions.appcompat}" |
| 149 | +} |
| 150 | +``` |
| 151 | + |
| 152 | +- 重启Android Studio,项目里就会多出一个名为buildSrc的module。 |
| 153 | + |
| 154 | + |
| 155 | + |
| 156 | + |
| 157 | +缺点: |
| 158 | + |
| 159 | +- buildSrc 依赖更新将重新构建整个项目,项目越大,重新构建的时间就越长,造成不必要的时间浪费。 |
| 160 | + |
| 161 | + |
| 162 | + |
| 163 | +### 脚本文件命名 |
| 164 | + |
| 165 | +- 用Groovy编写的Gradle build文件使用.gradle文件扩展名。 |
| 166 | +- 用Kotlin编写的Gradle build文件使用.gradle.kts文件扩展名。 |
| 167 | + |
| 168 | +### 常见误区 |
| 169 | + |
| 170 | +**用于定义字符串的双引号**。Groovy允许使用单引号来定义字符串,而Kotlin则要求使用双引号。 |
| 171 | + |
| 172 | +**基于句点表达式的字符串插值。**在Groovy中,您可以使用`$`前缀来表示基于句点表达式的字符串插值,例如以下代码段中的$project.rootDir: |
| 173 | + |
| 174 | +``` |
| 175 | +myRootDirectory = "$project.rootDir/tools/proguard-rules-debug.pro" |
| 176 | +``` |
| 177 | + |
| 178 | +但在Kotlin中,上述代码将对 `project`(而非 `project.rootDir`)调用 `toString()`。如需获取根目录的值,请使用大括号括住整个变量: |
| 179 | + |
| 180 | +- `myRootDirectory = "${project.rootDir}/tools/proguard-rules-debug.pro"` |
| 181 | +- **变量分配**。一些在Groovy中适用的分配方式现在会被视作setter(或者,对于列表、集合等,则适用“addX”),因为属性在Kotlin中是只读的。 |
| 182 | + |
| 183 | +### 显式和隐式buildTypes |
| 184 | + |
| 185 | +在Kotlin DSL中,某些buildTypes(如debug和release)是隐式提供的。但是,其他buildTypes则必须手动创建。 |
| 186 | + |
| 187 | +例如,在Groovy中,您可能有debug、release和staging` `buildTypes: |
| 188 | + |
| 189 | +**Groovy** |
| 190 | + |
| 191 | +```groovy |
| 192 | +buildTypes debug { ... } release { ... } staging { ... } |
| 193 | +``` |
| 194 | + |
| 195 | +在KTS中,仅debug和release` `buildTypes是隐式提供的,而staging则必须由您手动创建: |
| 196 | + |
| 197 | +**KTS** |
| 198 | + |
| 199 | +``` |
| 200 | +buildTypes getByName("debug") { ... } getByName("release") { ... } create("staging") { ... } |
| 201 | +``` |
| 202 | + |
| 203 | +### 使用plugins代码块 |
| 204 | + |
| 205 | +如果您在build文件中使用plugins代码块,IDE将能够获知相关上下文信息,即使在构建失败时也是如此。IDE可使用这些信息执行代码补全并提供其他实用建议,从而帮助您解决KTS文件中存在的问题。 |
| 206 | + |
| 207 | +在您的代码中,将命令式apply plugin替换为声明式plugins代码块。Groovy中的以下代码: |
| 208 | + |
| 209 | +```groovy |
| 210 | +apply plugin: 'com.android.application' |
| 211 | +apply plugin: 'kotlin-android' |
| 212 | +apply plugin: 'kotlin-kapt' |
| 213 | +apply plugin: 'androidx.navigation.safeargs.kotlin' |
| 214 | +``` |
| 215 | + |
| 216 | +…在 KTS 中变为以下代码: |
| 217 | + |
| 218 | +```kotlin |
| 219 | +plugins { |
| 220 | + id("com.android.application") |
| 221 | + id("kotlin-android") |
| 222 | + id("kotlin-kapt") |
| 223 | + id("androidx.navigation.safeargs.kotlin") |
| 224 | +} |
| 225 | +``` |
| 226 | + |
| 227 | +如需详细了解plugins代码块,请参阅 [Gradle 的迁移指南](https://docs.gradle.org/nightly/userguide/migrating_from_groovy_to_kotlin_dsl.html#applying_plugins)。 |
| 228 | + |
| 229 | + |
| 230 | + |
| 231 | + |
| 232 | + |
| 233 | +## Composing builds |
| 234 | + |
| 235 | + |
| 236 | + |
| 237 | + |
| 238 | + |
| 239 | +Composing builds:A composite build is simply a build that includes other builds. In many ways a composite build is similar to a Gradle multi-project build, except that instead of including single projects, complete builds are included. |
| 240 | +复合构建只是包含其他构建的构建. 在许多方面,复合构建类似于Gradle多项目构建,不同之处在于,它包括完整的builds,而不是包含单个projects: |
| 241 | + |
| 242 | +- 组合通常独立开发的构建,例如,在应用程序使用的库中尝试错误修复时 |
| 243 | +- 将大型的多项目构建分解为更小,更孤立的块,可以根据需要独立或一起工作 |
| 244 | + |
| 245 | + |
| 246 | + |
| 247 | +**使用方式** |
| 248 | + 1.新建module,名为versionPlugin(自起) |
| 249 | + 2.在该module下的build.gradle文件中,添加如下代码: |
| 250 | + |
| 251 | +```groovy |
| 252 | +buildscript { |
| 253 | + repositories { |
| 254 | + jcenter() |
| 255 | + } |
| 256 | + dependencies { |
| 257 | + // 因为使用的 Kotlin 需要需要添加 Kotlin 插件 |
| 258 | + classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:1.4.10" |
| 259 | + } |
| 260 | +} |
| 261 | +
|
| 262 | +apply plugin: 'kotlin' |
| 263 | +apply plugin: 'java-gradle-plugin' |
| 264 | +
|
| 265 | +repositories { |
| 266 | + // 需要添加 jcenter 否则会提示找不到 gradlePlugin |
| 267 | + jcenter() |
| 268 | + google() |
| 269 | +} |
| 270 | +
|
| 271 | +
|
| 272 | +compileKotlin { |
| 273 | + kotlinOptions { |
| 274 | + jvmTarget = "1.8" |
| 275 | + } |
| 276 | +} |
| 277 | +compileTestKotlin { |
| 278 | + kotlinOptions { |
| 279 | + jvmTarget = "1.8" |
| 280 | + } |
| 281 | +} |
| 282 | +
|
| 283 | +gradlePlugin { |
| 284 | + plugins { |
| 285 | + version { |
| 286 | + // 在app模块需要通过id引用这个插件 |
| 287 | + id = 'com.hi.dhl.plugin' |
| 288 | + // 实现这个插件的类的路径 |
| 289 | + implementationClass = 'com.hi.dhl.plugin.Deps' |
| 290 | + } |
| 291 | + } |
| 292 | +} |
| 293 | +``` |
| 294 | + |
| 295 | +3.在versionPlugin/src/main/java/包名/目录下新建Deps.kt文件,添加你的依赖配置,如: |
| 296 | + |
| 297 | +```groovy |
| 298 | +package com.hi.dhl.plugin |
| 299 | +
|
| 300 | +class Deps : Plugin<Project> { |
| 301 | + override fun apply(project: Project) { |
| 302 | + // Possibly common dependencies or can stay empty |
| 303 | + } |
| 304 | +
|
| 305 | + companion object { |
| 306 | + val appcompat = "androidx.appcompat:appcompat:1.1.0" |
| 307 | + } |
| 308 | +} |
| 309 | +``` |
| 310 | + |
| 311 | +5.在settings.gradle文件内添加includeBuild(“versionPlugin”),注意是includeBuild哦~,Rebuild项目 |
| 312 | +6.后面就可以在需要使用的gradle文件中使用了,如app下的build.gradle,在首行添加以下内容: |
| 313 | + |
| 314 | +```groovy |
| 315 | +plugins { |
| 316 | + // 这个id就是在versionPlugin文件夹下build.gradle文件内定义的id |
| 317 | + id "com.hi.dhl.plugin" |
| 318 | +} |
| 319 | +``` |
| 320 | + |
| 321 | +注意: plugins{}需要放在app模块build.gradle文件内的首行位置 |
| 322 | + |
| 323 | +使用如下: |
| 324 | + |
| 325 | +```groovy |
| 326 | +android { |
| 327 | + implementation Deps.appcompat |
| 328 | +} |
| 329 | +``` |
| 330 | + |
| 331 | + |
| 332 | + |
| 333 | + |
| 334 | + |
| 335 | +# 参考 |
| 336 | + |
| 337 | +- [Kotlin + buildSrc for Better Gradle Dependency Management](https://handstandsam.com/2018/02/11/kotlin-buildsrc-for-better-gradle-dependency-management/) |
| 338 | +- [How to use Composite builds as a replacement of buildSrc in Gradle](https://medium.com/bumble-tech/how-to-use-composite-builds-as-a-replacement-of-buildsrc-in-gradle-64ff99344b58) |
| 339 | +- [Stop using Gradle buildSrc. Use composite builds instead](https://proandroiddev.com/stop-using-gradle-buildsrc-use-composite-builds-instead-3c38ac7a2ab3) |
| 340 | +- [再见吧 buildSrc, 拥抱 Composing builds 提升 Android 编译速度](https://juejin.cn/post/6844904176250519565) |
| 341 | +- [composite_builds](https://docs.gradle.org/current/userguide/composite_builds.html) |
| 342 | + |
| 343 | +--- |
| 344 | + |
| 345 | + |
| 346 | +- Good Luck! |
0 commit comments