Gradle的介绍
Gradle是一个基于Apache Ant和Apache Maven概念的项目自动化建构工具。它使用一种基于Groovy的特定领域语言(DSL)来声明项目设置,抛弃了基于XML的各种繁琐配置。
面向Java应用为主。当前其支持的语言限于Java、Groovy和Scala,计划未来将支持更多的语言。
Gradle入门知识
projects 和 tasks是Gradle中最重要的两个概念,任何一个Gradle构建都是由一个或者多个project组成,每个project可以是一个jar包,一个web应用,或者一个Android app等,每个project又由多个task构成,一个task其实就是构建过程中一个原子性的操作,比如编译、拷贝等。
一个build.gradle文件是一个构建脚本,当运行gradle命令的时候会从当前目录查找build.gradle文件来执行构建。下面我们来看下gradle的Hello World。在build.gradle构建文件中输入以下构建脚本:
task hello {
doLast {
println 'Hello world!'
}
}
task定义了一个任务,这个任务名字是hello。doLast是Task的方法,意思是在该hello任务执行之后作的事情,可以用一个闭包配置它,这里是输出Hello world!字符串。我们在终端里执行如下命令运行查看结果:
$gradle hello -q
Hello world!
1
2
1
2
Android Studio的结构
这里新建一个android项目,选择Project结构模式,下面是项目的结构示意图
├── ApplicationName #项目路径
│ ├── .gradle
│ ├── .idea
│ ├── app #Android App目录
│ │ ├── build #构建输出目录
│ │ ├── libs#so相关库
│ │ ├── src #源代码,资源等
│ │ └── .gitignore
│ │ └── app.im
│ │ └── buidle.gradle#构建脚本
│ │ └── proguard-rules.pro#proguard混淆配置
│ ├── build
│ │ ├── intermediates
│ ├── gradle
│ │ ├── wrapper
│ └── .gitignore
│ └── buidle.gradle #工程构建文件
│ └── gradle.properties#gradle的配置
│ └── gradlew #gradle wrapper linux shell脚本
│ └── gradlew.bat
│ └── local.properties #配置Androod SDK位置文件
│ └── MyApplication.iml
│ └── settings.gradle #工程配置
├── External Liraies#类库、jar等
settings.gradle用于配置project,标明其下有几个module,比如这里p一个:ape
module
include ':app'
1
1
build.gradle(Project:projectName)是一个顶级的build配置文件,在这里可以为所有project以及module配置一些常用的配置。
// Top-level build file where you can add configuration options common to all sub-projects/modules.
buildscript {
repositories {
//使用jcenter库
jcenter()
}
dependencies {
// 依赖android提供的1.3.0的gradle build
classpath 'com.android.tools.build:gradle:1.3.0'
// NOTE: Do not place your application dependencies here; they belong
// in the individual module build.gradle files
}
}
//为所有的工程的repositories配置为jcenter
allprojects {
repositories {
jcenter()
}
}
task clean(type: Delete) {
delete rootProject.buildDir
}
build.gradle(Module:moduleName)用于module的配置,也是最重要的部分
apply plugin: 'com.android.application'
android {
compileSdkVersion 23
buildToolsVersion "23.0.2"
defaultConfig {
applicationId "com.example.zqw.myapplication"
minSdkVersion 15
targetSdkVersion 23
versionCode 1
versionName "1.0"
}
buildTypes {
release {
minifyEnabled false
proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'
}
}
}
dependencies {
compile fileTree(dir: 'libs', include: ['*.jar'])
testCompile 'junit:junit:4.12'
compile 'com.android.support:appcompat-v7:23.1.0'
compile 'com.android.support:design:23.1.0'
}
1
apply plugin: ‘com.android.application’:表示该module是一个app module,应用了com.android.application插件,如果是一个android library,那么这里的是apply plugin: ‘com.android.library’。
compileSdkVersion :是你SDK的版本号,也就是API Level,例如API-19、API-20、API-21等等
buildToolsVersion “23.0.2”:是你构建工具的版本,其中包括了打包工具aapt、dx等等。这个工具的目录位于..your_sdk_path/build-tools/XX.XX.XX这个版本号一般是API-LEVEL.0.0。 例如I/O2014大会上发布了API20对应的build-tool的版本就是20.0.0在这之间可能有小版本,例如20.0.1等等。
defaultConfig:是默认配置,如果没有其他的配置覆盖,就会使用这里的。看其属性的名字就可以知道其作用,比如applicationId是配置包名的,versionCode是版本号,versionName是版本名称等。
buildTypes是构建类型,常用的有release和debug两种,可以在这里面启用混淆,启用zipAlign以及配置签名信息等。
dependencies不属于Android专有的配置了,它定义了该module需要依赖的jar,aar,jcenter库信息。
Gradle配置签名
Gradle 配置应用的签名信息
这些配置放在上面提到的build.gradle(Module:moduleName)里面
signingConfigs {//签名配置这里配置了release 对应的还可以有debug
release {
storeFile file("keystore.jks")//这个文件需要放在modele所在根目录下
keyAlias "testapp"
keyPassword "111111"
storePassword "111111"
}
}
buildTypes {
release {
signingConfig signingConfigs.release//不要忘了要在release的时候加入我么的签名配置信息
minifyEnabled false
proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'
}
}
使用Gradle签名
第一种方式是使用Gradle工具,双击下图选择的命令即可,在控制台看到BUILD SUCCESSFUL就表明签名成功了,因为我们没有配置文件的生成路径所以会在默认地址里面C:\project\demo\MyApplication\app\build\outputs\apk
第二种方式使用命令。打开Terminal选项卡,用最下方标签切换。打开之后我们先敲一下gradle –help命令验证一下是否可以执行gradle命令。如果不可以的话,在环境变量里边配置一下gradle的path。
gradle assembleRelease
Gradle配置proguard混淆
我们可以为不同的buildTypes选择是否启用混淆,一般release发布版本是需要启用混淆的,这样别人反编译之后就很难分析你的代码,而我们自己开发调试的时候是不需要混淆的,所以debug不启用混淆。对release启用混淆的配置如下:
android {
buildTypes {
release {
minifyEnabled true
proguardFile 'proguard.cfg'
}
}
}
minifyEnabled为true表示启用混淆,proguardFile是混淆使用的配置文件,这里是module根目录下的proguard.cfg文件
在上面我们配置签名信息采用的是默认写法。下面是对默认写法的解释
proguard-android.txt是sdk中groguard默认的文件,具体地址在:/opt/sdk/tools/proguard/proguard-android.txt
proguard-rules.pro是AS中专用的proguard配置文件,其实只是后缀名不同,与Eclipse中的proguard-project.txt是一样的,配置规则相同
老版本开启混淆的命令是runProguard,现在统一用minifyEnabled命令了,将其设为true就好了。
Gradle启用zipAlign
zipalign是Android SDK中包含一个的工具,它能够对打包的应用程序进行优化。在你的应用程序上运行zipalign,使得在运行时Android与应用程序间的交互更加有效率。因此,这种方式能够让应用程序和整个系统运行得更快。强烈推荐在新的和已经发布的程序上使用zipalign工具来得到优化后的版本——即使你的程序是在老版本的Android平台下开发的。
在Android中,每个应用程序中储存的数据文件都会被多个进程访问:安装程序会读取应用程序的manifest文件来处理与之相关的权限问题;Home应用程序会读取资源文件来获取应用程序的名和图标;系统服务会因为很多种原因读取资源(例如,显示应用程序的Notification);此外,就是应用程序自身用到资源文件。
在Android中,当资源文件通过内存映射对齐到4字节边界时,访问资源文件的代码才是有效率的。但是,如果资源本身没有进行对齐处理(未使用zipalign工具),它就必须回到老路上,显式地读取它们——这个过程将会比较缓慢且会花费额外的内存。
对于应用程序开发者来说,这种显式读取方式是相当便利的。它允许使用一些不同的开发方法,包括正常流程中不包含对齐的资源,因此,这种读取方式具有很大的便利性(本段的原始意思请参考原文)。
遗憾的是,对于用户来说,这个情况恰恰是相反的——从未对齐的apk中读取资源比较慢且花费较多内存。最好的情况是,Home程序和未对齐的程序启动得比对齐后的慢(这也是唯一可见的效果)。最坏的情况是,安装一些未对齐资源的应用程序会增加内存压力,并因此造成系统反复地启动和杀死进程。最终,用户放弃使用如此慢又耗电的设备。
开启zipAlign配置如下
android {
buildTypes {
release {
zipAlignEnabled true
}
}
}
Gradle删除无用资源
在项目多次版本迭代后有一部分资源文件可能不在使用了,如果以前负责的人离开或者不在负责这个了,那么后面接收的人就不敢删除,这样安装包就会越来越大,以前使用工具可以找到没被使用的资源。现在android gradle也直接支持,配置如下
android {
buildTypes {
release {
shrinkResources true
}
}
}
多渠道打包
多渠道这个也是android平台特有的,因为有360手机助手、应用宝、小米应用市场等等大量类似AppStore的应用商城。在项目发布以后需要对后台数据分渠道统计,所以才有了多渠道打包。Android Gradle给我们提供了productFlavors,让我们可以对生成的APK包进行定制
android {
productFlavors {
dev{
}
google{
}
baidu{
}
}
}
这样当我们运行assembleRelease的时候就会生成3个release包,分别是dev、google以及baidu的。目前看这三个包除了文件名没有什么不一样,因为我们还没有定制,使用的都是defaultConfig配置。这里的flavor和defaultConfig是一样的,可以自定义其applicationId、versionCode以及versionName等信息,比如区分不同包名:注意实际发布项目APP的报名应该是唯一的,这里这是为了说明可以改
android {
productFlavors {
dev{
applicationId "org.flysnow.demo.dev"
}
google{
applicationId "org.flysnow.demo.google"
}
baidu{
applicationId "org.flysnow.demo.baidu"
}
}
}
- 批量修改生成的apk文件名
在我们打包发版的时候,一次性打几十个包,这时候我们就想让生成的apk文件名有区分,比如一眼就能看出这个apk是哪个版本的,哪个渠道的,是哪天打的包等等,这就需要我们在生成apk文件的时候动态修改生成的apk文件名达到这一目的。
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(),
"testapp_${variant.flavorName}_v${variant.versionName}_${buildTime()}.apk")
output.outputFile = apkFile
}
}
}
}
}
}
以baidu渠道为例,以上的代码会生成一个名字为testapp_baidu_v9.5.2.6_20150330.apk安装包。下面我们分析一下,Android Gradle任务比较复杂,它的很多任务都是自动生成的,为了可以更灵活的控制,Android Gradle提供了applicationVariants、libraryVariants以及testVariants,他们分别适用于app、library、app和library都适用。
这里是循环处理每个applicationVariant,当他们的输出文件名以apk结尾并且buildType是release时,重新设置新的输出文件名,这样就达到了我们批量修改生成的文件名的目的。
Gradle使用AndroidManifest里的占位符
这里用到的占位符也是让动态打包的APK包内配置信息和渠道对于,用于第三方的统计分析,以友盟统计为例
<meta-data android:value="${UMENG_CHANNEL_VALUE}" android:name="UMENG_CHANNEL"/>
1
1
如果是单一渠道我们可以直接给value赋值比如value=“baidu”,这样在项目代码中去获取UMENG_CHANNEL值就知道是哪一个渠道了,因为打的渠道包非常的多所以我们可以采用上面占位符方式
下面是gradle的配置
android {
defaultConfig {
manifestPlaceholders = [UMENG_CHANNEL_VALUE: 'dev']
}
}
1
2
3
4
5
1
2
3
4
5
我们的默认配置里AndroidManifest的${UMENG_CHANNEL_VALUE}占位符会被dev这个字符串所替换,也就说默认运行的版本是一个开发板。以此类推,我们其他渠道的版本就可以这样定义:
android {
productFlavors {
google{
applicationId "org.flysnow.demo.google"
manifestPlaceholders.put("UMENG_CHANNEL_VALUE",'google')
}
baidu{
applicationId "org.flysnow.demo.baidu"
manifestPlaceholders.put("UMENG_CHANNEL_VALUE",'baidu')
}
}
}
这样写依然过于繁琐,我们可以直接用渠道名称来给这个占位符赋值
productFlavors.all { flavor ->
manifestPlaceholders.put("UMENG_CHANNEL_VALUE",name)
}
自定义你的BuildConfig
在我们实际项目开发中一般会有测试环境和正式环境的区分(和服务器有交互的APP),一般我们会写一个配置文件,根据不同情况来访问不同的环境。以前的时候我们通过把不同的配置文件打包进APK中来控制,Android Gradle可以动态生成BuildConfig.java,我们只需要填写信息即可,使用起来更加方便。
android {
defaultConfig {
buildConfigField 'String','API_SERVER_URL','"http://www.myweb.com/"'
}
productFlavors {
google{
buildConfigField 'String','API_SERVER_URL','"http://www.google.com/"'
}
baidu{
buildConfigField 'String','API_SERVER_URL','"http://www.baidu.com/"'
}
}
}
buildConfigField 一共有3个参数,第一个是数据类型,就是你定义的常量值是一个什么类型,和Java的类型是对等的,这里是String。第二个参数是常量名,这里是API_SERVER_URL。第三个参数是常量值。如此定义之后,就会在BuildConfig.java中生成一个常量名为API_SERVER_URL的常量定义。默认配置的生成是:
public final static String API_SERVER_URL = "http://www.myweb.com/"
当是baidu和google渠道的时候生成的就是http://www.myweb.com/了。这个常量可以在我们编码中引用。在我们进行打包的时候会根据Gradle配置动态替换。
我们发现一般渠道版本都是用来发布的,肯定用的是生产服务器,所以我们可以使用批处理来搞定这个事情,而不用在一个个渠道里写这些配置。
productFlavors.all { flavor ->
buildConfigField 'String','API_SERVER_URL','"http://www.flysnow.org/"'
}
此外,比如Gradle的resValue,也是和buildConfigField,只不过它控制生成的是资源,比如我们在android的values.xml定义生成的字符串。可以用它来动态生成我们想要的字符串,比如应用的名字,可能一些渠道会不一样,这样就可以很灵活的控制自动生成,关于resValue详细介绍请参考相关文档,这里不再举例说明。
插装测试覆盖率代码
dexOptions javaMaxHeapSize
在Gradle 进行dex的可能会遇到内