Gradle for Android学习笔记(二)
Gradle之管理多个Module编译
在一个工程项目中,我们可能会有多个Module,如:多个app,library。
我们来看下一个最简单的多个Module的build文件结构。
如果我们的app项目中依赖多个lib,这样在项目中显示的会有点乱,我们可以通过一个目录管理它们,如:
1.在setting.gradle文件中:
include ':app',':libraries:library1',':libraries:library2'
2.在app项目的build.gradle文件中:
dependencies{
compile project(':libraries:library1')
}
Integrating Android Wear(整合穿戴设备)
如果你的app项目需要支持可穿戴设备,你可能需要在项目中添加一个Android Wear module。
在Android Wear项目中的build.gradle文件中
dependencies{
compile fileTree(dir: 'libs', include: ['*.jar'])
compile 'com.google.android.support:wearable:1.1.0'
compile 'com.google.android.gms:play-services-wearable:6.5.87'
}
在app项目中的build.gradle文件中
dependencies{
wearApp project(":wear") //wear是Android Wear项目的名称
}
这个wearApp的配置确保是Android Wear的module的apk文件,添加到最终的Android app项目的打包的APK中。
快速编译多个module
在Project工程的gradle.properties文件中
org.gradle.parallel = true //加快编译,其实这个配置默认被注释的,只要打开注释就好
Gradle会尝试根据CPU核数选择正确的线程数量。为了防止可能出现的问题,从同一个模块并行执行两个任务,每一个线程拥有一个完整的模块。
Module coupling(Module耦合)
在一个项目中有多个Module,我们可以把一些共有的配置抽取出来,放到root Project工程的build.gradle文件中allprojects{}中,如:
allprojects{
apply plugin:'com.android.application'
android{
compileSdkVersion 24
buildToolsVersion "24.0.0"
}
}
或者是使用ext{}的方式(详情见上篇)时,
如果这是你使用gradle.properties文件中的配置,如:
org.gradle.parallel=true
就会导致编译失败,原因是org.gradle.parallel=true会对每一个build任务采用一个线程执行(根据CPU的核心线程数分配),所以需要保持每个Module中的build任务独立,采用allproperties和ext{}方式在build的时候任务是耦合的。
多任务Tasks执行的顺序
定义2个任务task1和task2
task task1<<{
println 'task1'
}
task task2<<{
println 'task2'
}
- mustRunAfter
task2.runMustAfter task1 //task2执行在task1后面
执行命令:
gradlew task2 task1
log打印结果:
:task1
task1
:task2
task2
- dependsOn
task2.dependsOn(task1) 或者 task2.dependOn task1 //task2任务依赖于task1,执行task2就会先执行task1
执行命令
gradlew task2
log打印结果:
:task1
task1
:task2
task2
自定义任务保护签名信息的私密性
我们在设置签名配置的时候,一般是通过明码写入在build.gradle文件中的。
如果拿到build.gradle文件就会暴露我们的keypassword等私密信息。
我们可以通过将这些私密信息写到一个private.properties文件中,通过load这个文件中的配置来设置keypassword等私密信息。
1.在Project的根目录下,创建一个private.properties文件,并写好配置信息。
如:
release.password = newpassword
2.在Module中的build.gradle文件中创建一个任务
task getReleasePassword<<{
def password=""
if(rootProject.file('private.properties').exists()){
Properties properties = new Properties();
properties.load(rootProject.file('private.properties').newDataInputStream()) //加载文件
password = properties.getProperty('release.password') //获取配置文件中的release.password的值,复制给password变量
}
}
3.做一个判断,只针对release版本
task.whenTaskAdded{theTask->
if(theTask.name.equals("packageRelease")){
theTask.dependsOn "getReleasePassword"
}
}
4.在buildTypes{}中的release{}配置中使用签名配置
android.signingConfig.release.storePassword = password
android.signingConfig.release.keyPassword = password
Automatically renaming APKs(自动重命名apk)
android.applicationVariants.all{variant ->
variant.output.each{output ->
def file = output.outputFile
if (outputFile != null && outputFile.name.endsWith('.apk')) {
output.outputFile = new File(file.parent,file.name.replace(".apk","-${variant.versionName}.apk"))
}
}
}
减小APK文件体积
ProGuard
混淆工具,通过minifyEnabled设置true,开启混淆。ProGuard混淆工具修改类名和字段名保护apk的安全,同时也会压缩apk的体积。
android{ buildTypes{ release{ minifyEnabled = true ... } } }
Shrinking resources
去除没有使用过的resource,通过shrinkResource设置true。它可以在混淆的时候知道哪些resource使用了,如果resource没有使用,会在混淆时过滤。
android{ buildTypes{ release{ ... shrinkResource = true } } }
Manual shrinking:
如果你的app中仅支持1,2种语言,但是可能引用的lib库包含多种其他语言的strings资源,这个时候我们可以通过resConfig指定我们需要的strings资源。
android{ defaultConfig{ resConfigs 'en','cn' } }
同样我们可以指定我们需要支持的某些设备屏幕尺寸。如:
android{ defaultConfig{ resConfigs "hdpi","xhdpi","xxhdpi","xxxhdpi" } }
这样就可以过滤一些不要的资源。
Speeding up builds(加快编译速度)
Gradle properties
通过设置Gradle的属性配置的方式(在Project工程下的gardle.properties文件中配置相应的属性),如:
org.gradle.parallel = true
parallel build是通过CPU的核心线程数来给每一个工程合理的分配一个独立的线程进行编译。
org.gradle.daemon = true
在Android Studio,Gradle daemnon 是默认true的。它的意思是,你第一次编译后,下一次编译就会更快一些。如果你使用命令行编译,deamon值是不可用的,除非你设置org.gradle.daemon属性true。
org.gradle.jvmargs = -Xms256m -Xmx1024m
jvmargs给JVM内存池设置不同的内存值。
Xms:设置初始的内存值,256m代表内存大小。
Xmx:设置最大的内存值,1024m代表内存大小。
org.gradle.configureondemand = true
configureondemand:如果设置为true,在运行配置信息之前,Gradle会试着算出modules中的configuration那些改变了,哪些没有改变。
它会试着跳过modules中不执行的tasks,限制一些是小的configguration。
如果你的项目中有多个module,这个属性对你来说是比较有用的。
Profiling
如果你想找到build慢的原因,你可以给Gradletask 添加一个–profile标签,当你执行task时,Gradle会创建一个Profiling报告,告诉你build的那个部分最耗时。这个报告以html格式保存在build/reports/profile目录。
Jack and Jill(不推荐使用)
Jack(Java Android Compiler Kit)
Jack是一个新的Android编译工具,可以直接将Java源码直接编译成dex文件格式。他有自己的.jack库。
Jill(Java Intermediate Library Linker)
Jill是一个可以将.aar或.jar文件转成.jack库工具。
这2个工具提高了编译时间,简化了编译过程,但是不推荐使用。
Jack的使用
android{ defaultConfig{ useJack = true } } 或者 android{ productFlavors{ store1{ useJack = false } store2{ useJack = true } } }
Ignoring Lint
当我们执行release编译时,Lint在编译的时候可能会找到一些错误,会导致Gradle编译失败,Gradle可以忽略Lint错误,阻止中断编译过程,如:
android{
lintOptions{
abortOnError false //默认true,false表示错误不中断编译
}
}
Gradle 运行Ant任务
Gradle是直接支持运行一个标准的Ant
task archive<<{
ant.echo 'Ant is acthiving......'
ant.zip(destfile:'archive.zip'){
fileset(dir:'zipme')
}
}
这个task使用了2个Ant任务,echo和zip。
导入Ant脚本
如果你创建了一个Ant脚本,你可以使用ant.importBuild导入编译配置。所有的Ant targets会自动转成Gradle的task。
例如:
Ant的编译文件build.xml
<project>
<target name="hello">
<echo>Hello Ant</echo>
</target>
<project>
你可以导入Gradle编译中:
ant.importBuild 'build.xml'
执行命令与结果:
gradlew hello
打印结果:
:hello
[ant:hello] Hello Ant
Ant task和Gradle task一起使用
hello{
println 'Hello, Ant. It\'s me, Gradle'
}
执行 gradlew hello
打印结果如下:
:hello
[ant:hello] Hello Ant
Hello, Ant. It's me, Gradle
Gradle task依赖Ant task,如:
task hi(dependsOn:hello)<<{
println 'Hi'
}
执行gradlew hi
打印结果
:hello
[ant:echo] Hello Ant
Hello Gradle
:hi
Hi
Ant task依赖Gradle task,如:
<target name="hello" depends="hi">
<echo>Hello Ant</echo>
</target>
执行gradlew hello
打印结果:
:hi
Hi
:hello
[ant:echo] Hello Ant
Hello Gradle
如果你的Ant编译文件中,包含比较多的task,为了避免覆盖,可能需要重命名,如:
ant.importBuild('build.xml'){antTargatName->
'ant-' + antTargetName
}
如果你的ant task重命名了,注意在与Gradle task相互依赖时,需要将ant task名字改过来。不然会报UnKnownTaskExecption。
Ant Properties
Ant还可以通过build.xml在Gradle中定义属性,如:
<target name="appVersion">
<echo>${version}</echo>
</target>
在Gradle中定义version的属性
ant.version = '1.0'
或者
ant.properties['version'] = '1.0'
执行命令:
gradlew appVersion
执行结果:
:appVersion
[ant:echo] 1.0
Split Apk
设备是允许通过屏幕密度(density)或者是ABI(application binary interface)来拆分apk的。
- 通过density进行split apk
android{
split{
density{
enabled true
exclude 'ldpi','mdpi'
compatibleScreen 'normal','large','xlarge'
}
}
}
生成结果:
app-hdpi-release.apk
app-universal-release.apk
app-xhdpi-release.apk
app-xxhdpi-release.apk
app-xxxhdpi-release.apk
- 通过ABI进行split apk
android{
split{
abi{
enabled true
include 'armeabi','x86','mips'
universaiApk true
}
}
}