很久没有关注Android的最新动态,最近帮朋友做一个Android项目,才又重操旧业。发现Google官方已经全面从ADT切到了Android Studio,自然的,Gradle成了官方的编译工具。其实很早就在用Gradle了,在这里做一个总结,祭奠我逝去的Android生涯(真的逝去了吗)。
Gradle的基本使用
当前推荐版本和安卓插件版本
如果是新建的项目,推荐使用最新版本;如果是接手已有的项目,那就保持现有的,升级可能会有一些小麻烦,这个不多说。
使用gradle模板创建安卓项目
下面是用android sdk创建一个用gradle模板的安卓项目(一般情况下用IDE就可以了):
1
| android create project -n test -t android-19 -p test -k com.test -a Test -g -v 0.12.0
|
下图是建好的目录结构:build.gradle就是构建配置脚本;gradlew和gradle目录是一个wrapper,用来在没有gradle的环境下来build时,代替gradle命令,直接可以使用./gradlew
而其他目录结构也和用ant或者maven创建的略有不同,比如AndroidManifest的位置等等,在下面映射目录一节具体介绍。
最简单的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
| buildscript { repositories { mavenCentral() } dependencies { classpath 'com.android.tools.build:gradle:0.12.0' } } apply plugin: 'android'
android { compileSdkVersion 'android-19' buildToolsVersion '20.0.0'
buildTypes { release { runProguard false proguardFile getDefaultProguardFile('proguard-android.txt') } } }
|
gradle build后就生成build目录,build/outputs/apk是生成的apk,而.gradle目录是一个cache目录,这2个目录都建议加入到gitignore里面。
映射目录 sourceSets
比较传统的目录结构是AndroidManifest.xml, src, res, libs, assets目录都是在项目根目录的,下面的配置就可以指定不同的目录的具体位置。可以使用安卓的目录结构,而不是更深的java风格的项目目录层级。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15
| android { ...
sourceSets { main { manifest.srcFile 'AndroidManifest.xml' java.srcDirs = ['src'] aidl.srcDirs = ['src'] res.srcDirs = ['res'] assets.srcDirs = ['assets'] jniLibs.srcDirs = ['libs'] } } ... }
|
gradle本身是可以编程的语言,所以很多事情可以通过编写函数来增强。上文提到手写sdk和buildtools的版本是很烦的一件事情,下面的函数就可以自动检测本地环境。具体的实现见下面链接的gist。
1 2 3 4 5 6
| android { ... compileSdkVersion highestSdkAvailable(20) buildToolsVersion latestBuildToolsAvailable("20.0.0") ... }
|
配置签名 signingConfigs
配置release包所有的签名,里面的参数要用真正的值换掉
1 2 3 4 5 6 7 8 9 10 11 12
| android { ... signingConfigs { release { storeFile file("/your.keydir/your.keystore") storePassword "your.password" keyAlias "your.key.alias" keyPassword "your.alias.passord" } } ... }
|
配置 buildTypes
buildTypes其实就是不同的任务类型,对应安卓来说,最常见的是下面的release,beta,debug三种类型。
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
| android { ... buildTypes {
release { proguardFile 'proguard-project.txt' signingConfig signingConfigs.release jniDebugBuild false debuggable false runProguard true zipAlign true }
beta { proguardFile 'proguard-project.txt' signingConfig signingConfigs.release jniDebugBuild false debuggable true runProguard false zipAlign true }
debug { applicationIdSuffix ".debug" jniDebugBuild true debuggable true zipAlign true } ... }
|
上面release包和beta包,同样使用release签名,是方便测试,具体可以根据实际情况来调整混淆及其他开关。debug包加后缀的目的,是可以同时安装debug和release的包,也是为了方便调试的。
通过上面的配置,就有了一个基本的安卓项目的构建脚本了。运行gradle build就可以打出来全部的包了。
Gralde依赖管理
gradle真正强大的地方不在于比较人性化简明的语法,而是在方便灵活的依赖管理。
配置repo和flatDir
下面的repositories需要在build.gradle的顶级节点配置,不能写在android或buildscript里面。
1 2 3 4 5 6 7 8 9 10 11 12 13 14
| repositories { mavenCentral() mavenLocal() maven { url "http://mente.github.io/facebook-api-android-aar" } flatDir { dirs 'aars' } }
|
配置远程依赖和本地依赖
1 2 3 4 5 6 7 8 9 10 11 12 13 14
| dependencies { compile 'com.android.support:support-v4:20.0.0' compile ('fr.avianey:facebook-android-api:3.16.0@aar') { transitive = true } compile 'com.jakewharton:butterknife:5.1.2' compile files('libs/umeng_sdk.jar') compile fileTree(dir: 'libs', include: ['*.jar']) }
|
模块项目依赖 settings.gradle
因为现在的安卓项目越来越复杂,所以模块化是趋势,而且依赖的库有时候是开源的,也涉及到二次开发的需求。settings.gradle的模式就能很好的支撑模块化的复杂依赖构建的场景。
首先,在项目根目录下编写build.gradle, settings.gradle文件,而其他模块项目,主项目,都是二级目录。
1 2 3 4 5 6
| android-demo: |__________ app |__________ app-lib |__________ facebook-sdk |__________ build.gradle |__________ setings.gradle
|
根目录的build.gradle可以配置所有模块都有的基本配置,如下面的例子:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17
| buildscript { repositories { mavenCentral() } dependencies { classpath 'com.android.tools.build:gradle:0.12.+' } }
allprojects { repositories { mavenCentral() } tasks.withType(Compile) { options.encoding = 'UTF-8' } }
|
根目录的settings.gradle是配置build时都包含那些模块
1 2 3
| include ':facebook-sdk' include ':app-lib' include ':app'
|
然后就是在每个模块的子目录里面去配置各自的build.gradle就好了,但是要添加依赖的模块。比如上面app目录的build.gradle就得添加相应的依赖。
1 2 3 4 5 6
| dependencies { ... compile project(':facebook') compile project(':app-lib') ... }
|
Gralde小技巧
集成ndk-build
在目录映射那里设置了jniLibs.srcDirs = [‘libs’]的话,默认里面的so会自动打包到apk里。但是如果想在gradle build时,重新编译ndk的代码的话,就需要加入下面的配置
1 2 3 4 5 6 7 8 9 10 11 12
| import org.apache.tools.ant.taskdefs.condition.Os
task ndkBuild(type: Exec) { if (Os.isFamily(Os.FAMILY_WINDOWS)) { commandLine 'ndk-build.cmd', '-j' } else { commandLine 'ndk-build', '-j' } } tasks.withType(JavaCompile) { compileTask -> compileTask.dependsOn ndkBuild }
|
忽略lint的警告 lintOptions
1 2 3 4 5 6
| android { lintOptions { checkReleaseBuilds false abortOnError false } }
|
重命名生成的apk
1 2 3 4 5 6 7
| android { defaultConfig { applicationVariants.all { variant -> appendVersionNameVersionCode(variant) } } }
|
productFlavors和渠道包
productFlavors其实是另外一个维度,和buildTypes是相乘的关系。我们可以利用这点来打安卓的渠道包。
1 2 3 4 5 6 7 8 9
| android { productFlavors { googleplay{ } wandoujia{ } ... } }
|
但是其实无论是umeng还是GA,flurry等渠道包,有更加节能高效的打渠道包方案: 利用aapt add的方式,注入assets文件,达到改变渠道的目的。(比flavors和基于apktool的方案都要完美,当然还有直接用apk末尾增加信息的方式,也是不错的选择,这里不做展开了。)
发布组件
很多公司都自己利用Sonatype Nexus项目搭建了自己的maven库,那么一些模块库,也是有打包后上传到maven企业源的需求的。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17
| apply plugin: 'maven'
ext.pomCfg = { name 'app-lib' description 'project_desc' ... }
uploadArchives.repositories.mavenDeployer { repository(url: 'repo_url') { authentication( userName: repo_user, password: repo_passwd) ... } pom.project pomCfg }
|
常用命令参数
1 2 3 4 5 6 7 8
| gradle --help gradle tasks gradle asD (gradle assembleDebug) gradle asR (gradle assembleRelease) gradle asD --refresh-dependencies gradle asD --parallel gradle asD --parallel-threads 3 gradle clean
|