本篇文章来自本人以前的学习笔记,本来笔记语言比较生硬,之前有同事问我关于Gradle的知识,对我的笔记评价还不错。于是趁最近有时间对语言稍加润色,发出来供大家参考。这篇笔记来自《巧用Gradle构建Android应用》(《Gradle Recipes for Android》)这本书(以前从学校图书馆借的书,怕忘记一些知识点,所以做了笔记)。
Android Studio使用Gradle做构建、打包和部署。采用脚本语言Groovy。
1.1 认识Gradle构建文件
- 示例1-1 setting.gradle
文件里面
include ':app'
include语句表示app子目录是唯一的子项目。如果你添加一个Android库项目,其也会被添加到这个文件。
- 示例1-2 build.gradle
文件顶层。
// Top-level build file where you can add configuration options common to all sub-projects/modules.
//你可以在构建文件顶层添加所有子项目或模块通用的配置选项
buildscript {
repositories {
google()
jcenter()
maven { url 'https://maven.google.com' }
}
dependencies {
classpath 'com.android.tools.build:gradle:3.0.1'
// NOTE: Do not place your application dependencies here; they belong
// in the individual module build.gradle files
//注意:不要把你的应用程序依赖放在这里,它们属于各独立模块的build.gradle文件
}
}
allprojects {
repositories {
jcenter()
google()
maven {
credentials {
username 'username'
password 'password'
}
url 'http://repo.mycompany.com/maven2'
}
}
}
task clean(type: Delete) {
delete rootProject.buildDir
}
Gradle本身默认不包含Android的功能,Google为Gradle提供了一个Android的插件,其简化了Android项目的配置。构建文件顶层的buildscript
块告诉Gradle去哪下载这个插件。
allprojects
段顶层项目和所有子项目都默认使用 jcenter() 仓库来下载所有的Java第三方依赖库。
gradle允许你自定义你的task,并插入到有向无环图(DAG)(例如task clean方法)。
- 示例1-3 app的build.gradle
apply plugin: 'com.android.application'
android {
compileSdkVersion 27
buildToolsVersion "26.0.2"
defaultConfig {
applicationId "com.kousenit.myandroidapp"
minSdkVersion 19
targetSdkVersion 27
versionCode 1
versionName "1.0"
}
buildTypes {
release {
minifyEnabled false
proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'
}
}
compileOptions {
sourceCompatibility JavaVersion.VERSION_1_7
targetCompatibility JavaVersion.VERSION_1_7
}
}
dependencies {
implementation fileTree(include: ['*.jar'], dir: 'libs')
implementation 'com.android.support:appcompat-v7:27.0.2'
implementation 'com.squareup.retrofit2:retrofit:2.3.0'
testImplementation 'junit:junit:4.12'
}
fileTreey依赖意味着libs目录下所有以.jar结尾的文件都被添加到了编译类路径。
implementation 'com.android.support:appcompat-v7:27.0.2'
意味着Gradle从Android支持的库中添加版本为27.0.2的appcompat-v7 jar文件。注意-v7意味着支持Android应用程序Android 7及以后版本,并不是库本身的版本7这个库被定义为编译时依赖,因此其所有的类在整个项目都可用。
顶层Android构建文件通过buildscript
块将Android的Gradle插件添加到了你的项目中。模块的构建文件通过“apply”应用这个插件,因此将Android块添加到Gradle的DSL(领域特定语言)中。Android项目使用com.android.application插件。
1.2 认识SDK版本以及其他的默认值
compileSdkVersion | 指定目标编译环境 |
---|---|
buildToolsVersion | 指定构建工具版本 |
applicationId | 应用程序的“package”名字 |
minSdkVersion | 应用程序支持的最小Android SDK版本 |
targetSdkVersion | 应用程序目标的Android版本 |
versionCode | 数值代表你的app相对于其他的版本(应用升级时候用) |
versionName | 代表app发行版本,面向用户的 |
compileOptions | 这个app期望使用JDK1.7 |
1.3 运行Gradle的方法(
Linux(./gradlew),Windows(gradlew))
查看gradle可以使用的任务:
E:\work\gradletest>gradlew task
Starting a Gradle Daemon (subsequent builds will be faster)
> Task :tasks
------------------------------------------------------------
All tasks runnable from root project
------------------------------------------------------------
Android tasks
-------------
androidDependencies - Displays the Android dependencies of the project.
signingReport - Displays the signing info for each variant.
sourceSets - Prints out all the source sets defined in this project.
Build tasks
-----------
assemble - Assembles all variants of all applications and secondary packages.
assembleAndroidTest - Assembles all the Test applications.
assembleArro - Assembles all Arro builds.
assembleDebug - Assembles all Debug builds.
assembleFriendly - Assembles all Friendly builds.
assembleRelease - Assembles all Release builds.
build - Assembles and tests this project.
buildDependents - Assembles and tests this project and all projects that depend on it.
buildNeeded - Assembles and tests this project and all projects it depends on.
clean - Deletes the build directory.
cleanBuildCache - Deletes the build cache directory.
compileArroDebugAndroidTestSources
compileArroDebugSources
compileArroDebugUnitTestSources
compileArroReleaseSources
compileArroReleaseUnitTestSources
compileFriendlyDebugAndroidTestSources
compileFriendlyDebugSources
compileFriendlyDebugUnitTestSources
compileFriendlyReleaseSources
compileFriendlyReleaseUnitTestSources
mockableAndroidJar - Creates a version of android.jar that's suitable for unit tests.
Build Setup tasks
-----------------
init - Initializes a new Gradle build.
wrapper - Generates Gradle wrapper files.
Help tasks
----------
buildEnvironment - Displays all buildscript dependencies declared in root project 'gradletest'.
components - Displays the components produced by root project 'gradletest'. [incubating]
dependencies - Displays all dependencies declared in root project 'gradletest'.
dependencyInsight - Displays the insight into a specific dependency in root project 'gradletest'.
dependentComponents - Displays the dependent components of components in root project 'gradletest'. [incubating]
help - Displays a help message.
model - Displays the configuration model of root project 'gradletest'. [incubating]
projects - Displays the sub-projects of root project 'gradletest'.
properties - Displays the properties of root project 'gradletest'.
tasks - Displays the tasks runnable from root project 'gradletest' (some of the displayed tasks may belong to subprojects).
Install tasks
-------------
installArroDebug - Installs the DebugArro build.
installArroDebugAndroidTest - Installs the android (on device) tests for the ArroDebug build.
installArroRelease - Installs the ReleaseArro build.
installFriendlyDebug - Installs the DebugFriendly build.
installFriendlyDebugAndroidTest - Installs the android (on device) tests for the FriendlyDebug build.
installFriendlyRelease - Installs the ReleaseFriendly build.
uninstallAll - Uninstall all applications.
uninstallArroDebug - Uninstalls the DebugArro build.
uninstallArroDebugAndroidTest - Uninstalls the android (on device) tests for the ArroDebug build.
uninstallArroRelease - Uninstalls the ReleaseArro build.
uninstallFriendlyDebug - Uninstalls the DebugFriendly build.
uninstallFriendlyDebugAndroidTest - Uninstalls the android (on device) tests for the FriendlyDebug build.
uninstallFriendlyRelease - Uninstalls the ReleaseFriendly build.
Verification tasks
------------------
check - Runs all checks.
connectedAndroidTest - Installs and runs instrumentation tests for all flavors on connected devices.
connectedArroDebugAndroidTest - Installs and runs the tests for arroDebug on connected devices.
connectedCheck - Runs all device checks on currently connected devices.
connectedFriendlyDebugAndroidTest - Installs and runs the tests for friendlyDebug on connected devices.
deviceAndroidTest - Installs and runs instrumentation tests using all Device Providers.
deviceCheck - Runs all device checks using Device Providers and Test Servers.
lint - Runs lint on all variants.
lintArroDebug - Runs lint on the ArroDebug build.
lintArroRelease - Runs lint on the ArroRelease build.
lintFriendlyDebug - Runs lint on the FriendlyDebug build.
lintFriendlyRelease - Runs lint on the FriendlyRelease build.
lintVitalArroRelease - Runs lint on just the fatal issues in the arroRelease build.
lintVitalFriendlyRelease - Runs lint on just the fatal issues in the friendlyRelease build.
test - Run unit tests for all variants.
testArroDebugUnitTest - Run unit tests for the arroDebug build.
testArroReleaseUnitTest - Run unit tests for the arroRelease build.
testFriendlyDebugUnitTest - Run unit tests for the friendlyDebug build.
testFriendlyReleaseUnitTest - Run unit tests for the friendlyRelease build.
To see all tasks and more detail, run gradlew tasks --all
To see more detail about a task, run gradlew help --task
BUILD SUCCESSFUL in 26s
1 actionable task: 1 executed
1.4 添加Java库的依赖
gradle依赖的完整语法testImplementation group:'junit',name:'junit',version:'4.12'
依赖的快捷语法testImplementation 'junit:junit:4.12'
版本号以变量形式(不推荐)testImplementation 'junit:junit:4.+'
gradle默认遵循传递依赖,如果你想针对一个特定的库关掉这个功能,使用transitive
标志。
implementation ('com.squareup.retrofit2:retrofit:2.4.0'){
transitive = false
}
如果你想要一个模块的jar,不引入任何额外的依赖,你也可以像这样指定
implementation 'org.codehaus.groovy:groovy:3.0.0-alpha-3@jar'
你也可以从dependencies块中排除一个传递依赖
implementation ('org.spockframework:spock-core:1.2-RC1-groovy-2.5'){
exclude group: 'org.codehaus.groovy'
exclude group: 'junit'
}
1.5 配置仓库
任何一个Maven仓库都可以使用maven并且跟随一个url块来添加到默认的仓库列表中去。
repositories {
google()
jcenter()
maven{
url 'http://repo.spring.io/milestone'
}
}
通过密码保护的仓库可以使用一个credentials 块。
maven{
credentials{
username 'username'
password 'password'
}
url 'http://repo.spring.io/milestone'
}
如果你有本地文件系统的文件,你可以通过flatDir 语法将一个文件目录作为一个仓库。
repositories {
google()
jcenter()
maven{
credentials{
username 'username'
password 'password'
}
url 'http://repo.spring.io/milestone'
}
flatDir{
dirs 'lib'
}
}
这是除了显式的使用 files 或fileTree 添加文件到dependencies 块的另一种方式。
2.1 属性设置
ext属性(使用ext块配置通用的值)—-“ext”代表 “extra”。例如:
ext {
AAVersion = '1.1.2'
}
implementation "com.android.support.constraint:constraint-layout:$AAVersion"
如果你想要将实际的值挪出构建文件(用户名,密码)
原代码:
credentials{
username 'username'
password 'password'
}
可以把它添加到项目根目录下的gradle.properties文件:
login = 'user'
pass = '123456'
然后
credentials{
username login
password pass
}
你还可以通过命令行设置这些值,通过使用-P传参给gradle。
gradlew -Plogin = me -Ppassword = password assembleDebug
使属性动态变化:
ext{
if(!project.hasProperty('login'){
login = 'login_from_build_file'
}
if(!project.hasProperty('pass'){
pass = 'pass_from_build_file'
}
}
这些也可以通过命令行来设置,并且拥有最高优先级。
2.2 在项目之间分享设置
allprojects {
repositories {
jcenter()
}
}
此段代码对于这个默认项目来说意味着顶层项目和app模块。
另一个做法是使用subproject
块,例如多个库项目在自己的构建文件中应用library
插件,可以通过在顶层应用这个插件来消除重复。
subprojects{
apply plugin: `com.android.library`
}
2.3 为发布apk签名
默认情况下,调试用的秘钥存储库在你的用户目录下的.Android
子目录下面。默认的名字叫做debug.keystore
,并且其密码是Android。
一个构建类型决定app如何被打包。Gradle的Android插件默认支持两种不同类型的构建:debug和release。
buildTypes {
release {
minifyEnabled false
proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'
}
}
其中,minifyEnabled代表在打包app时自动删除无用的资源。如果设置成true,Gradle还会删除无需用到的依赖库。这只会在shrinkResources属性同时设置成true时生效。
buildTypes {
release {
//打开代码压缩
minifyEnabled true
//打开资源压缩
shrinkResources true
proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'
}
}
为了能够在一台机器上安装多种构建类型,Android必须能够分辨出它们的应用程序ID。applicationIDSuffix允许Gradle生成多个APK,每个都有自己的ID。
buildTypes {
release {
minifyEnabled false
proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'
signingConfig signingConfigs.config
}
debug{
applicationIdSuffix '.debug'
versionNameSuffix '-debug'
}
}
现在发布版本和调试版本的app都可以被部署到同一台设备上了。
1. 产品定制和变种
如果我们想要构建同一个应用程序,但是使用不同的资源或类。这时要进行产品定制。
使用Android闭包的productFlavors块来声明一个产品定制。
假设一个”Hello,World”风格的Android app,基于简单的EditText输入名字来欢迎用户。你可以通过引入“friendly”,“arro”来给这个app一个态度。
productFlavors{
friendly{
applicationId 'com.test.friendly'
}
arro{
applicationId 'com.test.arro' +
''
}
}
我们可以有多个定制。在这个例子中,每个定制拥有一个略微不同的applicationId,所以这些定制可以被安装到同一个设备上。
每个产品定制可以拥有其自己的值,如下面的属性,还有一些其他的基于默认的defaultConfig的相同属性。
- applicationId
- minSdkVersion
- targetSdkVersion
- versionCode
- versionName
- sigingConfig
每个定制定义了自己的源代码集和资源,其与主代码集是兄弟关系。在我们的示例中,意味着除了app/src/main/java以外,你还可以添加源文件在如下目录中:
- app/src/arro/java
- app/src/friendly/java
你还可以添加额外的资源文件到如下目录:
- app/src/arro/res
- app/src/friendly/res/layout
- app/src/friendly/res/values
所有任何res的子目录也是一样的。同样的资源目录结构也可以应用到所有的定制。
为了部署一个特定的变种,Android Studio提供了一个Build Variant视图。从下拉框中选择合适的变种,并像平常一样部署。
2. 合并资源
如果我们要在同一个Activity展示不同的文字,应该如何设置呢?其实所有的定制都有自己的资源目录,在appres下。在这里,一个叫做values的子目录被添加进去了,一个叫做strings.xml的文件拷贝从app/src/main/res/values拷贝进去了。arro的定制string.xml如下所示。
<resources>
<string name="app_name">GradleTeststring>
<string name="hello_world">I\'m
Arrostring>
resources>
通过合并项目定制和构建类型的res文件夹以及主要的目录树来合并资源。优先级是:构建类型覆盖产品定制,其覆盖main代码集。
在我们的MainActivity展示一段文字
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="@string/hello_world"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintLeft_toLeftOf="parent"
app:layout_constraintRight_toRightOf="parent"
app:layout_constraintTop_toTopOf="parent" />
再看看我们Friendly定制的Strings.xml
<resources>
<string name="app_name">GradleTeststring>
<string name="hello_world">I\'m Friendlystring>
resources>
运行之后就可以显示不同定制的文字了。
3. 定制维度
一个产品定制还不够,你需要另一个标准类区分你应用程序的不同版本。
//添加定制维度 attitude和client
defaultConfig {
applicationId "com.example.tangx.gradletest"
minSdkVersion 15
targetSdkVersion 27
versionCode 1
versionName "1.0"
testInstrumentationRunner "android.support.test.runner.AndroidJUnitRunner"
flavorDimensions "attitude", "client"
// flavorDimensions "versionCode"
}
productFlavors {
arrogant {
dimension 'attitude'
applicationId 'com.kousenit.helloworld.arrg'
}
friendly {
dimension 'attitude'
applicationId 'com.kousenit.helloworld.frnd'
}
obsequious {
dimension 'attitude'
applicationId 'com.kousenit.helloworld.obsq'
}
stark {
dimension 'client'
}
wayne {
dimension 'client'
}
}
此处有两个维度定制:attitude和client。arrogant、friendly和obsequious定制在attitude维度上的,stark和wayne是client类型的定制。
这个组合会生成很多变种。如(obsequious)下:
- obsequiousStarkDebug
- obsequiousStarkRelease
- obsequiousWayneDebug
- obsequiouswayneRelease
两种构建类型,三种态度的定制和两种客户的定制,一共产生2 * 3 * 2=12不同的变种。
4. 合并不同定制间的java源代码
如果想要为单独的产品定制添加Android activities或者其他java类,可以用此方法。
虽然定制和构建类型中的字符串和布局资源与main源码集中的值会相应的覆盖,java类却又不同。如果main源码集中的代码引用了一个特定的类每个定制和构建类型可以有自己的实现,只要在main中没有就行。
从Gradle的视角,Android库就是根项目的子项目。意味着它们就像Android应用程序一样,但是在一个子目录中。被添加的模块的名字因此被添加到setting.gradle文件中。
例如一个添加了模块的settings.gradle文件
include ':app',':icndb'
在这个例子中,Android库叫做icndb。
每个库都有自己的Gradle构建文件,其支持与根项目相同的设置。你可以指定最小和目标SDK版本、自定义构建类型、添加构建定制,并且修改依赖。重要的区别是Gradle构建使用了不同的插件。
//使用library插件
apply plugin: 'com.android.library'
android {
compileSdkVersion 25
buildToolsVersion "25.0.2"
packagingOptions{
//从多个依赖中移除冲突文件
exclude 'META-INF/notice.txt'
exclude 'META-INF/license.txt'
exclude 'LICENSE.txt'
}
defaultConfig {
minSdkVersion 16
targetSdkVersion 25
versionCode 1
versionName "1.0"
}
buildTypes {
release {
minifyEnabled false
proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'
}
}
}
dependencies {
compile fileTree(include: ['*.jar'], dir: 'libs')
testCompile 'junit:junit:4.12'
compile 'com.android.support:appcompat-v7:25.3.1'
compile 'com.squareup.retrofit2:retrofit:2.1.0'
compile 'com.squareup.retrofit2:converter-gson:2.1.0'
}
在app编译时使用icndb库
dependencies {
compile project(':icndb')
}
构建过程本身生成调试和发布版本的库,在icndb/build/outputs/aar目录下。
org.gradle.deamon = true
Gradle团队强烈推荐你不要在持续集成服务器上使用守护进程,在那里稳定和重复构建比性能更有价值。
2. 并行编译
Gradle有一个“incubating”选项来并行编译独立的项目。在gradle.properties文件中添加如下配置:
org.gradle.parallel = true
注意这里可能并没有用,大多数Android项目的模块都是相互关联的,使得并行编译并不能带来很多的好处。
3. 按需配置
org.gradle.configureondemand = true
大多数Android程序都只要很少数量的子项目,所以这个功能可能也不会很实用。
4. 修改JVM设置
最终Gradle构建会在一个Java进程中进行,所以影响JVM性能的因素也会影响Gradle的性能。如下演示Java虚拟机的一个方便的设置。
rg.gradle.jvmargs=-Xmx4096m -XX:MaxPermSize=512m -XX:+HeapDumpOnOutOfMemoryError -Dfile.encoding=UTF-8
-Xmx标志指定这个Java进程使用的最大内存数量。一个-Xmx标志指示了最初为这个进程分配的内存数量。这个例子还修改了“永久代”的空间大小,并且当java.lang.OutOfMemoryError发生时,将堆中的情况导出到一个文件中。
5. 使用dex选项
Android块允许你设置一个选项来控制转换java二进制代码(如.class文件)为Dalvik可执行文件(.dex文件)的“dex”进程。Android块内部的dexOptions块包含了如下选项:
dexOptions{
javaMaxHeapSize '2g'
jumboMode = true
preDexLibraries = true
}
使用javaMaxHeapSize
作为dx运行时最大堆大小指定Xmx值得一种替代方式,增量为1024m。所以这里设置为2g。
应用“jumbo模式”运载在dex文件中出现大量的字符串。如果这是个问题,你可能想要花更多的时间配置ProdGuard。
preDexLibraries会提前在库上运行dex进程,正如其所听起来的那样。如文档所说,“这可以提高增量构建性能,但是单纯的构建可能会更慢”
5. 剖析你的构建
你可以在命令行运行Gradle时加上–profile的选项来生成关于构建的有用信息。结果会在build/reports/profile目录下以HTML的形式被写入。命令如下:
gradlew --profile assembleDebug
多个标签将总结报告分成了几个独立的配置步骤,配置和执行。在这样大小的项目中这里看不到太多的东西,但是在大型项目中,这是一个很好的发现构建过程瓶颈的方式。
——“Gradle Recipes for Android by Ken Kousen”