0%

How to Use Gradle

Gradle is a powerful tool than you can image!
Accelerate developer productivity

  1. What’s the Gradle?
  2. Why use Gradle?
  3. How to use Gradle?
  4. Pitfalls when use Gradle
  5. Best pratice of Gradle

What’s the Gradle?

Gradle 是以Groovy为基础,面向java应用,基于DSL语法的自动化构建工具。是google引入,替换ant和maven的新工具,其依赖兼容maven和ivy。

Gradle(英[g’reɪdl])是一个任务驱动型的构建工具,是一个依赖管理工具,更是一个编程框架。
它抛弃了基于XML的各种繁琐配置,取而代之的是一种基于Groovy的内部领域特定(DSL)语言。
在android studio中,我们使用这个工具可以完成app的编译打包等工作。

Basic Gradle Component

每一个build.gradle文件代表着一个Project。Tasks在build.gradle中定义。当初始化构建进程时,gradle会基于build文件,集合所有的Project和Tasks,一个Tasks包含了一系列动作,然后它们将会按照顺序执行,一个动作就是一段被执行的代码,很像Java中的方法。

Gradle实际上是一个容器,实现真正的功能的都是Gradle的插件Plugin,而Plugin中又定义了各式各样的Task,这一个个的Task是执行任务的基本单元。

Project

每一个待编译的工程(可以是一个jar包,一个web应用,或者一个android app等)都称为一个Project。

Task

每一个Project在构建的时候都包含一系列的Task。一个Task其实就是构建过程中一个原子性的操作。比如一个Android APK的编译可能包含:

  • Java源码编译Task、
  • 资源编译Task、
  • JNI编译Task、
  • lint检查Task、
  • 打包生成APK的Task、
  • 签名Task等。

Plugin

Gradle是一个框架,作为框架,它负责定义流程和规则。而具体的编译工作则是通过插件的方式来完成的。比如编译Java有Java插件,编译Groovy有Groovy插件,编译Android APP有Android APP插件,编译Android Library有Android Library插件。
简单来说,插件就是一系列任务的集合,主要作用是把一些重复利用的逻辑打包,这样就可以在不同的项目中可以重复的使用。
要使用插件,可以通过引入依赖的方式添加。

Why use Gradle?

From mobile apps to microservices, from small startups to big enterprises, Gradle helps teams build, automate and deliver better software, faster.

Build Anything


Write in Java, C++, Python or your language of choice. Package for deployment on any platform. Go monorepo or multi-repo. And rely on Gradle’s unparalleled versatility to build it all.

Automate Everything


Use Gradle’s rich API and mature ecosystem of plugins and integrations to get ambitious about automation. Model, integrate and systematize the delivery of your software from end to end.

Deliver Faster


Scale out development with elegant, blazing-fast builds. From compile avoidance to advanced caching and beyond, we pursue performance relentlessly so your team can deliver continuously.

Goals of the new Build System(使用gradle的目的)

  1. Make it easy to reuse code and resources
  2. Make it easy to create several variants of an 3. application, either for multi-apk distribution or for different flavors of an application
  3. Make it easy to configure, extend and customize the build process
  4. Good IDE integration

使用gradle的目的:
更容易重用资源和代码;
可以更容易创建不同的版本的程序,多个类型的apk包;
更容易配置,扩展;
更好的IDE集成;

How to use Gradle?

首先明确gradle跟maven一样,也有一个配置文件,maven里面是叫pom.xml,而在gradle中是叫build.gradle。Android Studio中的android项目通常至少包含两个build.gradle文件,一个是project范围的,另一个是module范围的,由于一个project可以有多个module,所以每个module下都会对应一个build.gradle。这么说有点抽象,看下面这个图:

Gradle Android插件用户指南翻译

Gradle Plugin User Guide 官方原文地址
http://tools.android.com/tech-docs/new-build-system/user-guide

中文版在线阅读地址
http://avatarqing.github.io/Gradle-Plugin-User-Guide-Chinese-Verision

简单来说有以下几点:

  1. 独立项目,和Google无关
  2. Gradle基于Groovy。(Maven、Ant基于xml)
  3. Groovy是拓展了Java语言的一种动态语言,语法更简洁,可以作为Java平台的脚本语言使用 ,拥有类似Python、Ruby和Smalltalk中的一些特性。
  4. Gradle是基于Groovy定义了一套DSL,所谓DSL(领域专用语言),就是专门针对某一特定问题的计算机语言。而Gradle我们可以认为是经过“定制”的Groovy,专门用于项目构建的语言。
  5. Gradle兼容Maven、Ant
  6. Gradle 的推出主要以 Java 应用为主,当然还支持 Android、C、C++等。

As如何依赖Gradle让Gradle作为自身的构建工具呢?

答:Google开发了一个Gradle插件,让As项目依赖这个插件,就相当于让Gradle作为自身的的构建工具。

现在比如我们新建一个As项目,打开项目的根目录的gradle.build文件。有如下代码:

project下的build.gradle是基于整个project的配置,主要配置gradle 版本及 全局依赖仓库、库或者其他全部参数。
android studio 现在重要仓库采用jcenter(),之前版本放在mavenCentral。
另外有时还没有加入jcenter()仓库的第三方库,也需要在这里配置他们的库地址。
需要在这里配置,才能将第三方库拉下来

1
2
3
4
5
6
7
8
9
buildscript {
repositories {
jcenter() //表示编译过程中依赖的仓库
}
dependencies {
//依赖android开发使用的gradle插件
classpath 'com.android.tools.build:gradle:2.2.0'
}
}

而要引入Android APP插件,就需要在build.gradle引用Android APP插件:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
//申明使用插件,表明要编译的内容和产物
apply plugin: 'com.android.application'

//配置插件属性
android {
compileSdkVersion 24
buildToolsVersion "24.0.1"
defaultConfig {
applicationId "zhj.gradledemo"
minSdkVersion 15
targetSdkVersion 24
versionCode 1
versionName "1.0"
testInstrumentationRunner "android.support.test.runner.AndroidJUnitRunner"
}
buildTypes {
release {
minifyEnabled false
proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'
}
}
}

Android其实就是写了两个插件:
com.android.application和com.android.library。
应用这两个插件就可以实现Android APP和Android Library的构建。

As项目中的Gradle

.gradle文件夹

.gradle文件夹 是gradle 运行以后生成的缓存文件夹。

Project中的build.gradle文件

project下的build.gradle是基于整个project的配置,主要配置gradle 版本及 全局依赖仓库、库或者其他全部参数。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
// Top-level build file where you can add configuration options common to all sub-projects/modules.

buildscript {
repositories {
//这里依赖的jcenter仓库是gradle脚本自身需要的资源
jcenter()
}
dependencies {
classpath 'com.android.tools.build:gradle:2.2.0'

// NOTE: Do not place your application dependencies here; they belong
// in the individual module build.gradle files
}
}

allprojects {
repositories {
//这里依赖的jcenter仓库是项目所有模块需要的资源
jcenter()
}
}

task clean(type: Delete) {
delete rootProject.buildDir
}

module中build.gradle文件

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
//申明使用插件,表明要编译的内容和产物
apply plugin: 'com.android.application'

android {
compileSdkVersion 24
buildToolsVersion "24.0.1"
//默认配置,会同时应用到debug和release版本上
defaultConfig {
applicationId "zhj.gradledemo"
minSdkVersion 15
targetSdkVersion 24
versionCode 1
versionName "1.0"
testInstrumentationRunner "android.support.test.runner.AndroidJUnitRunner"
}
buildTypes {
release {
minifyEnabled true //是否混淆
proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro' //混淆文件的位置
}
debug {
minifyEnabled false
}
}
// 多渠道
productFlavors {
//可以设置不同渠道渠道号,应用名称
pro {
}

fre {
}
}
}

//依赖第三方库
dependencies {
//编译libs目录下所以jar包
compile fileTree(include: ['*.jar'], dir: 'libs') //导入所有的jar包
androidTestCompile('com.android.support.test.espresso:espresso-core:2.2.2', {
exclude group: 'com.android.support', module: 'support-annotations'
})
compile 'com.android.support:appcompat-v7:24.2.0'
compile 'com.android.support:design:24.2.0'
testCompile 'junit:junit:4.12'
proCompile 'com.android.support:recyclerview-v7:24.2.0'
}

Project中setting.gradle

这个文件是全局的项目配置文件,里面主要声明Project中所包括的所有module

1
2
3
4
5
6
//一个Project中所包括的所有module
include ':Gotobus', ':android-support-v7-appcompat'
include ':google-play-services_lib'
include ':TakeTours'
include ':Common'
include ':CompanyCommon'

Project中gradle.properties

gradle.properties为gradle的配置文件,里面可以定义一些常量供build.gradle使用,比如可以配置签名相关信息如keystore位置,密码,keyalias等,build.gradle就可以直接引用
gradle 中的一些配置参数建议写到gradle.properties

1
2
3
4
5
6
7
8
9
10
11
12
13
14
//编译版本信息
APPLICATION_ID = com.jin.myAPP
COMPILE_SDK_VERSION = 23
BUILD_TOOLS_VERSION = 23.0.1
MIN_SDK_VERSION = 15
TARGET_SDK_VERSION = 1
VERSION_CODE = 1
VERSION_NAME = 1.0.0.0

//keystore信息
STORE_FILE = ../app/mykey.keystore
STORE_PASSWORD = your password
KEY_ALIAS = your alias
KEY_PASSWORD = your password

配置应用的签名信息

在android.signingConfigs{}下定义一个或者多个签名信息,然后在buildTypes{}配置使用即可。比如这里

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
android {

signingConfigs {
release {
storeFile file("release.keystore")
keyAlias "release"
keyPassword "123456"
storePassword "123456"
}
debug {
...
}
}

buildTypes {
release {
signingConfig signingConfigs.release
}
debug {
signingConfig signingConfigs.debug
}
}
}

storeFile是签名证书文件,keyAlias是别名,keyPassword是key的密码,storePassword是证书的密码。配置好相关信息即可在buildTypes配置使用。

一般重要的信息,例如签名信息,可以直接将信息写到gradle.properties,然后在build.gradle中引用即可。

buildTypes是指建构的类型,一般只用两种默认类型 debug 和 release ,顾名思义 debug 用来配置开发过程中的一些内容;release 用来配置正式发布版本的内容。有时我们需要发布介于debug与release之间的preview 版本。

Build Variant 差异管理

比如app生成不同版本(免费,收费),适配特殊机型,多渠道等需要发多个包,最终能编译出的apk的数量是由Product Flavor(产品种类)与Build Type(构建类型)决定的,
公式:Build Variant = Build Type x Product Flavor

BuildType(构建类型)

默认有debug和release两种,标示编译的类型,通常在混淆代码、可调式、资源压缩上做一些区分。

Product Flavor(产品种类)

为了满足“同一个project,根据一个很小的区分,来打不同的包”这个需求。实现多渠道打包。注意:这里的Flavor名如果是数字开头,必须用引号引起来。

调整module的目录结构sourceSets

默认情况下,java文件和resource文件分别在src/main/java和src/main/res目录下,在build.gradle文件的andorid{}里面添加下面的代码,便可以将java文件和resource文件放到src/java和src/resources目录下。

1
2
3
4
5
6
7
8
9
10
11
12
sourceSets {
main {
manifest.srcFile 'AndroidManifest.xml'
//设置java文件的位置
java.srcDirs = ['src']
resources.srcDirs = ['src']
aidl.srcDirs = ['src']
renderscript.srcDirs = ['src']
res.srcDirs = ['res']
assets.srcDirs = ['assets']
}
}

全局变量定义及引用

可以在顶层build.gradle脚本中定义一些全局变量,提供给子脚本引用

1
2
3
4
5
6
7
 ext {
// global variables definition
compileSdkVersion = 'Google Inc.:Google APIs:23'
buildToolsVersion = "23.0.3"
minSdkVersion = 14
targetSdkVersion = 23
}

子脚本引用

1
2
3
4
5
6
7
8
9
 android {
compileSdkVersion rootProject.ext.compileSdkVersion
buildToolsVersion rootProject.ext.buildToolsVersion

defaultConfig {
minSdkVersion rootProject.ext.minSdkVersion
targetSdkVersion rootProject.ext.targetSdkVersion
}
}

Gradle常用命令介绍

打开Android Studio内置的Terminal终端,输入如下命令

gradlew -v

指令代码 指令功能
gradlew 下载更新gradle
gradlew -v 查询版本号
gradlew clean 清理项目的output文件
gradlew check 运行检测和测试任务
gradlew build 运行check和assemble,检查依赖并编译打包(debug、release环境的包)
gradlew clean build 运行 clean 和 build 两个 gradle task
gradlew assemble 编译并打Debug和Release包
gradlew assembleDebug 编译并把本项目下所有模块所有渠道的Debug版本打包
gradlew assembleRelease 编译并把本项目下所有模块所有渠道的Release版本打包
gradlew assembleWandoujia 生成wandoujia渠道的Release和Debug版本
gradlew assembleWandoujiaRelease 打包wandoujia渠道的Release版本
gradlew assembleWandoujiaRelease -p app 打包app工程下wandoujia渠道的Release版本(使用-p选项,决定执行哪个工程)
gradlew installRelease Release模式打包并安装
gradlew uninstallRelease 卸载Release模式包

gradlew代表 gradle wrapper,意思是gradle的一层包装,大家可以理解为在这个项目本地就封装了gradle,即gradle wrapper。
在./gradle/wrapper/gralde-wrapper.properties文件中声明了它指向的目录和版本。只要下载成功即可用grdlew wrapper的命令代替全局的gradle命令。

assemble 命令创建task有如下语法:

允许直接构建一个Variant版本,例如assembleFlavor1Debug。
允许构建指定Build Type的所有APK,例如assembleDebug将会构建Flavor1Debug和Flavor2Debug两个Variant版本。
允许构建指定flavor的所有APK,例如assembleFlavor1将会构建Flavor1Debug和Flavor1Release两个Variant版本。

批量修改生成的apk文件名

在我们打包发版的时候,一次性打几十个包,这时候我们就想让生成的apk文件名有区分,比如一眼就能看出这个apk是哪个版本的,哪个渠道的,是哪天打的包等等,这就需要我们在生成apk文件的时候动态修改生成的apk文件名达到这一目的。代码如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
def buildTime() {
def date = new Date()
def formattedDate = date.format('yyyyMMdd')
return formattedDate
}

android {
buildTypes {
release {
applicationVariants.all { variant ->
variant.outputs.each { output ->
if (output.outputFile != null && output.outputFile.name.endsWith('.apk')
&&'release'.equals(variant.buildType.name)) {
def apkFile = new File(
output.outputFile.getParent(),
"Gtobus_${variant.flavorName}_v${variant.versionName}_${buildTime()}.apk")
output.outputFile = apkFile
}
}
}
}
}
}

以baidu渠道为例,以上的代码会生成一个名字为Gtobus__gotobus_v5.1.2_20161115.apk安装包。

这里是循环处理每个applicationVariant,当他们的输出文件名以apk结尾并且buildType是release时,重新设置新的输出文件名,这样就达到了我们批量修改生成的文件名的目的。

Pitfalls when use Gradle

项目中的问题

android studio的编译时屏蔽掉lint检查,可以避免由于编译条件太过严格而编译不过的问题:

1
2
3
lintOptions {
abortOnError false
}

如果遇到多个jar包中的某个文件冲突,可以在对应module下的build.gradle文件的android标签下加上如下属性:

1
2
3
4
packagingOptions {
exclude 'META-INF/NOTICE.txt'// 这里是具体的冲突文件全路径
exclude 'META-INF/LICENSE.txt'
}

资源文件和manifest的合并

资源和mainfests的优先级是这样的:

如果一个资源在main中和在flavor中定义了,那么那个在flavor中的资源有更高的优先级。这样那个在flavor文件夹中的资源将会被打包到apk。而在依赖项目申明的资源总是拥有最低优先级。

Best pratice of Gradle

Gradle编译优化

在添加依赖的时候尽量明确版本号,省去gradle查找最新版的时间

1
2
3
不要使用
compile ‘com.facebook.fresco:fresco:latest’
compile ‘com.facebook.fresco:fresco:1.+’,

使用daemon

构建初始化的很多工作是关于java虚拟机的启动,加载虚拟机环境,加载class文件等,如果这些动作交给一个单独的后台进程去做,那么,第一次初始化之后的修改代码再构建是不是可以节省很多时间呢?答案是肯定的,通过在gradle.properties加入这样一句来开启,如果想让修改全局所有项目都生效,那么修改这个文件~/.gradle/gradle.properties

1
org.gradle.daemon=true

并行构建模块化项目

将你的项目拆分成多个子项目并开启并行构建也是一个不错的主意,比如将相对独立的模块拆分成独立的库工程(Library projects),主工程(Application project)依赖这些库工程,这样的话,开启并行构建才会发挥作用。并行构建开启方式是修改文件gradle.properties,加入如下行:

1
org.gradle.parallel=true

ext配置全局参数

project的build.gradle中的ext可以为各位module进行全局配置参数,防止各个module之间的不统一,不可控。而且当我们升级sdk、build tool、target sdk等,几个module都要更改,非常的麻烦。

1
2
3
4
5
6
7
8
ext {
compileSdkVersion = 22
buildToolsVersion = "23.0.1"
minSdkVersion = 10
targetSdkVersion = 22
versionCode = 34
versionName = "v2.6.1"
}

然后在各自module的build.gradle中引用:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
android {

compileSdkVersion rootProject.ext.compileSdkVersion

buildToolsVersion rootProject.ext.buildToolsVersion

defaultConfig {

applicationId "com.xxx.xxx"

minSdkVersion rootProject.ext.minSdkVersion

targetSdkVersion rootProject.ext.targetSdkVersion

versionCode rootProject.ext.versionCode

versionName rootProject.ext.versionName
}
}

resValue 定义资源

例如resValue “string” 就是字符串资源,可以用R.String 来引用对应的字符串资源

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
android {
defaultConfig {
resValue "string", "build_time", buildTime()
resValue "string", "build_host", hostName()
resValue "string", "build_revision", revision()
}
}

def buildTime() {
return new Date().format("yyyy-MM-dd HH:mm:ss")
}

def hostName() {
return System.getProperty("user.name") + "@" +InetAddress.localHost.hostName
}

def revision() {
def code = new ByteArrayOutputStream()
exec {
commandLine 'git', 'rev-parse', '--short', 'HEAD'
standardOutput = code
}
return code.toString()
}

属性默认值

如果某个属性的值没有使用DSL设置,这个属性将会使用某些默认值,下表展示了默认值的处理过程。

属性名 DSL对象中的默认值 默认值

Property Name Default value in DSL object Default value
versionCode -1 value from manifest if present
versionName null value from manifest if present
minSdkVersion -1 value from manifest if present
targetSdkVersion -1 value from manifest if present
applicationId null value from manifest if present
testApplicationId null applicationId + “.test”
testInstrumentationRunner null android.test.InstrumentationTestRunner
signingConfig null null
proguardFile N/A (set only) N/A (set only)
proguardFiles N/A (set only) N/A (set only)

如果你想在build脚本中使用自定义的逻辑来查询这些属性,第二列中的值就很重要。比如,你可以编写如下的代码:

1
2
3
if (android.defaultConfig.testInstrumentationRunner == null) {  
// assign a better default...
}

如果属性的值仍然是null,那么在构建的时候,就会使用第三列的默认值,但是DSL元素中并不包含这些默认值,因此你不能在程序中查询这些值。这样做的目的是仅在必要的时候(构建时)才会去解析manifest内容。

[Android Studio编译慢、卡死和狂占内存怎么破?

](https://www.zhihu.com/question/27953288)

gradle 构建一个工程主要分为三部分(完全掌握了下面这张图,整个 gradle 的构建过程能了解个十之七八了):

  1. 初始化阶段:主要是解析 setting.gradle 文件(因此有人提到减少 setting.gradle 的 module 数量,是很有道理的,但是实际操作过程限制颇多,原因最后会大致说一下);

  2. 读取配置阶段:主要是解析所有的 projects 下的 build.gradle 文件,包括 rootProject 和其他的 subprojects(子项目),检查语法,确定 tasks 依赖以建立 task 的有向无循环图,检查 task 里引用的文件目录是否存在等(这一步也进一步验证了减少 setting.gradle 里的 module 数量可以加快编译速度,因为减少一个 module ,需要解析的 build.gradle 文件就减少一个,第 3 步里就不会执行本属于这个 module 的任务了,但是还是 1 里面说的问题,限制颇多);

  3. 执行阶段:按照 2 中建立的有向无循环图来执行每一个 task ,整个编译过程中,这一步基本会占去 9 成以上的时间,尤其是对于 Android 项目来讲,将 java 转为 class compileDebugJavaWithJavac/compileReleaseJavaWithJavac
    和 将 class 合并成 dex transformClassesWithDexForDebug/transformClassesWithDexForRelease
    这两步很耗时,第一步还好,第二步会耗时非常久。首先在 gradle.properties 里设置 org.gradle.jvmargs=-Xmx4096m //越大越好
    ,然后在工程的 build.gradle 里的 android 结点下增加 dexOptions 配置,如下:dexOptions {
    dexInProcess true
    preDexLibraries true
    javaMaxHeapSize “4g”//越大越好
    incremental true
    }

明确了 gradle 的生命周期,那么就可以看到加快编译速度的关键就是从第三步入手,当然,减少 setting.gradle 里的 modules 数量这一步也是必须的。下面说说我们公司的实践吧。项目插件化改造,每位业务上的同学只需要编译一个模块即可,这一点基本上从根本上解决了编译慢的问题(对于大多数没有插件化需求的朋友们可以看下面的一些实践),首先 setting.gradle 里的 module 只有自己开发的模块了,而对应的执行阶段的任务也只有这一个 module 的任务了。执行一次 gradle build ,我们就会发现,在这个过程中,其实是执行了多次打包任务的,在 buildTypes 里配置了多个编译打包类型,默认有 debug 和 release ,我们还可以手动配置其他的类型,而且还有 productFlavor 里的多渠道,这样就会执行多次编译打包,而正常开发过程中,只需要打 debug 包去调试,因此使用 gradle assembleDebug 即可,等发版的时候使用其他方式去打多渠道的包(如美团的方案http://tech.meituan.com/mt-apk-packaging.html);

既然编译主要时间都集中在 gradle 生命周期的第三步执行 task 任务里,那么我们就可以把一些无关紧要的任务给禁用掉,比如各种 Test ,各种 lint 等,刚好在 gradle 里有这样的指令 -x lint 可以临时禁掉 lint 任务,-x test 可以禁掉 test 任务,事实上对于一个稍微大一点的项目,lint 也是很耗时的,当然也可以通过 gradle 脚本彻底禁用 lint 和 test 任务,我也在一些微信群里分享过相关代码,但是不太建议这么做,因为有时候 lint 和 test 也是挺有用的;gradle 本身提供了一些指令参数可以加快编译,比如 –daemon ,开启守护进程,–parallel ,开启并行编译等,这个也可以在 gradle.propertites 里配置(编译使用的 jvm 内存也可以在这里配置)。定制 gradle 编译流程,利用官方提供的 API 完全可以定制一个适合自己的编译流程,可以参考一下携程的 DynamicAPK/sub-project-build.gradle at master · CtripMobile/DynamicAPK · GitHub,里面有携程他们自己整个完整的编译流程,脚本本身很简单,一共只有两三百行代码。

上面讲到的几点,现有环境就可以做到的大概是这样(有一点要特别注意,如果工程里有交叉依赖,一定不要使用 –parallel 参数):gradle assembleDebug –daemon –parallel -x lint -x test
,如果是要直接安装到设备上的话,就把 assembleDebug 换成 installDebug ,assembleDebug 可以简写为 asD ,installDebug 可以简写为 iD 。最后讲一下,为什么减少 setting.gradle 里的 module 数量,确实可以加快编译,但是却限制颇多呢?首先,我们想一下整个编译过程,先去解析 gradle 配置,建立 tasks 依赖有向图,然后再去执行每一个 module 的 task ,如果我们通过 maven 依赖,使用 aar 替掉了 module(单指 android library),如果我们要改这个 module 里的文件,岂不是每次都要修改上传再下载,这其实还好,但是有一个致命的问题:不修改版本号的话,SNAPSHOT 在 IDEA 里经常会不好使。这样就导致修改的东西会不生效,去解决这个问题是非常耗费时间的。不过有一种方式,可以一定程度上解决问题,增加下面的脚本:project.configurations.all(new Action() {
@Override
void execute(Configuration files) {
files.resolutionStrategy.cacheDynamicVersionsFor(5, TimeUnit.MINUTES)
files.resolutionStrategy.cacheChangingModulesFor(0, TimeUnit.SECONDS)
}
})

那有人会问,插件化里,每个人开发一个模块,对于每个模块的维护不也是要打包上传到 maven ,每次一有修改,哪怕是非常微小的修改,也要做一次上传,同样会遇到 SNAPSHOT 不好使的问题。嘿嘿,这个问题嘛,我司自己维护了一个 gradle 插件,已经解决了,至于解决方案,是公司机密,我是不会讲的。然后,还有一点,我相信大部分开发者平常开发都是单 module 的,多 module 的情况并不多,因此大多数依赖基本也都是 aar 或者 jar ,根本就不存在所谓的将 library 转成 aar 上传的情况,因此一些答主说的根本毫无意义,这也是为什么我会说影响编译速度的情况主要集中在 gradle 生命周期的第三个阶段,至于第三个阶段的优化,看我上面的答案就好了。

Reference

[1]优雅的Android Studio项目配置–常用库和版本管理
[2]优雅的管理第三方库
[3]Gradle从入门到了解
[4]Gradle for Android 问题总结
[5]Gradle User Guide 中文版
[6]Android Studio Gradle实践之多渠道自动化打包+版本号管理
[7]深入浅出Android Gradle构建系统
[8]在Android Studio中建立和维护团队通用库
[9]Gradle 完整指南(Android)
[10]Gradle User Guide 中文版
[11]Android Studio编译慢、卡死和狂占内存怎么破? [12] [Android Studio系列(三)]Android Studio 编译、同步慢的解决方法

欢迎关注我的其它发布渠道