Gradle总结
0x00 前言
Android studio默认使用Gradle构建Android工程。我之前有花一段时间研究Gradle,在此总结一下!
Gradle跟ant/maven一样,是一种依赖管理/自动化构建工具。但是跟ant/maven不一样,它并没有使用xml语言,而是采用了Groovy语言(语法类似于Java),这使得它更加简洁、灵活,更加强大。
Gradle的安装略,百度一大堆。
0x01 HelloWorld
先写个hello world吧!
task hello << {
println 'Hello world!'
}
执行段语句:gradle -q hello
,-q表示不显示log,相应的还可以 -info
,-debug
来查看详细的log信息。
然后输出的内容是:Hello world!
这就是一个简单的gradle文件。里面定义了一个task:
hello
。执行打印任务。当使用gradle命令行执行task时,每个task只会被执行一次,所以gradle test test和gradle test命令的执行结果是一模一样的。
0x02 Gradle Plugin
看了上面的例子,我们知道如何定义一个任务,但是在Android开发时我们看到的Gradle并没有定义task。因为在Android中我们默认添加了Gradle的Android插件:apply plugin: 'com.android.application'
。 com.android.application
中默认定义了我们一些常用的task:
build: 完整编译项目,包含编译,测试和创建jar包
clean: 删除build目录
assemble: 编译和创建jar包
check: 编译和测试代码
我们在一个gradle中可以使用多个插件。正如我们的项目使用crashlytics
进行bug收集统计,那么我们的项目中还得依赖apply plugin: 'io.fabric'
插件。
0x03 Android Gradle
在Android Studio新建Android项目现在默认使用gradle构建。使用gradle构建在规范的目录结构与以前ADT生成的有很大的不同了。Gradle要求的一些规范:工程源码位于$workspace/app/src/main/java,测试代码位于$workspace/app/src/test/java。所有位于$workspace/app/src/main/res文件都被包含到jar包中作为资源文件,所有的输出文件都会放在build目录里面,jar文件放在$workspace/app/build/libs目录下。
详细目录咱们可以通过命令gradlew app:sourceSets
来查看:
main
----
Compile configuration: compile
build.gradle name: android.sourceSets.main
Java sources: [app\src\main\java]
Manifest file: app\src\main\AndroidManifest.xml
Android resources: [app\src\main\res]
Assets: [app\src\main\assets]
AIDL sources: [app\src\main\aidl]
RenderScript sources: [app\src\main\rs]
JNI sources: [app\src\main\jni]
JNI libraries: [app\src\main\jniLibs]
Java-style resources: [app\src\main\resources]
对于添加工程依赖也方便了很多,咱们可以通过dependencies
来依赖工程,依赖生成的文件路径为:$workspace/app/build/intermediates/exploded-aar/***
。依赖格式如下:
dependencies {
compile group: 'org.hibernate', name: 'hibernate-core', version: '3.6.7.Final'
}
缩写形式 “group:name:version”.
dependencies {
compile 'org.hibernate:hibernate-core:3.6.7.Final'
}
note:-x命令用来排除一些命令的执行,比如gradle build -x ext,会在编译的时候不执行ext任务,即使build task依赖ext也不会执行, 但ext所依赖的task如果被其他task依赖是会执行的。
dependencies
DSL 元素是标准的 Gradle API 的一部分,不属于android 元素内。
dependencies {
compile files('libs/test.jar')
}
0x04 Gradlew
在项目的根目录下我们看到有gradlew/gradlew.bat文件,gradlew代表 gradle wrapper,意思是gradle的一层包装,大家可以理解为在这个项目本地就封装了gradle,即gradle wrapper, 在$workspace/gradle/wrapper/gralde-wrapper.properties
文件中声明了它指向的目录和版本。只要下载成功即可用grdlew wrapper的命令代替全局的gradle命令。第一次执行gradlew *时系统会下载创建项目时使用的gradle环境,时间会比较长。下载完后就可以直接像gradle一样使用了。
省略写法:gradle assembleRelease --> gradle aR
gradlew tasks
gradlew tasks --all
可以得到一个完整的任务列表,并且看到任务运行之间的依赖关系。
0x05 Gradle Build
Gradle编译过程如图:
build.png
我们可以查看gradle执行过程,比如我们要编译一个welove渠道的release包,执行gradle aWR >>1.txt
,>>1.txt表示将打印的内容重定向到记事本中,得到的内容:
:app:preBuild UP-TO-DATE
:app:preWeloveReleaseBuild UP-TO-DATE
:app:checkWeloveReleaseManifest
:app:preWeloveDebugBuild UP-TO-DATE
:app:prepareComAndroidSupportAppcompatV72200Library UP-TO-DATE
:app:preWeloveDebugAndroidTestBuild UP-TO-DATE
:app:prepareComAndroidSupportMultidex101Library UP-TO-DATE
:app:prepareComAndroidSupportRecyclerviewV72200Library UP-TO-DATE
:app:prepareComAndroidSupportSupportV42200Library UP-TO-DATE
:app:prepareWeloveReleaseDependencies
:app:compileWeloveReleaseAidl UP-TO-DATE
:app:compileWeloveReleaseRenderscript UP-TO-DATE
:app:generateWeloveReleaseBuildConfig UP-TO-DATE
:app:generateWeloveReleaseAssets UP-TO-DATE
:app:mergeWeloveReleaseAssets UP-TO-DATE
:app:generateWeloveReleaseResValues UP-TO-DATE
:app:generateWeloveReleaseResources UP-TO-DATE
:app:mergeWeloveReleaseResources UP-TO-DATE
:app:processWeloveReleaseManifest UP-TO-DATE
:app:processWeloveReleaseResources UP-TO-DATE
:app:generateWeloveReleaseSources UP-TO-DATE
:app:compileWeloveReleaseJavaWithJavac UP-TO-DATE
:app:compileWeloveReleaseNdk UP-TO-DATE
:app:compileWeloveReleaseSources UP-TO-DATE
:app:lintVitalWeloveRelease
:app:processWeloveReleaseJavaRes UP-TO-DATE
:app:transformResourcesWithMergeJavaResForWeloveRelease UP-TO-DATE
:app:transformClassesAndResourcesWithProguardForWeloveRelease UP-TO-DATE
:app:collectWeloveReleaseMultiDexComponents UP-TO-DATE
:app:transformClassesWithMultidexlistForWeloveRelease UP-TO-DATE
:app:transformClassesWithDexForWeloveRelease UP-TO-DATE
:app:mergeWeloveReleaseJniLibFolders UP-TO-DATE
:app:transformNative_libsWithMergeJniLibsForWeloveRelease UP-TO-DATE
:app:validateReleaseConfigSigning
:app:packageWeloveRelease UP-TO-DATE
:app:zipalignWeloveRelease UP-TO-DATE
:app:assembleWeloveRelease
BUILD SUCCESSFUL
Total time: 46.918 secs
对比上图可以体会gradle打包的过程!
0x06 Gradle Demo
==
放出我们项目工程的gradle,删除部分关键信息,给大家做个参考,如下:
android {
compileSdkVersion 22
buildToolsVersion "22.0.1"
defaultConfig {
applicationId "***"
minSdkVersion 10
targetSdkVersion 22
versionCode 1
versionName "1.0"
multiDexEnabled true
}
// 配置keystore签名
signingConfigs {
releaseConfig {
storeFile file('release-key.keystore')
keyAlias "releasekey"
storePassword "Replace Valid Store Password"
keyPassword "Replace Valid Key Password"
}
}
buildTypes {
debug {
}
release {
minifyEnabled true // 进行混淆
proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'
signingConfig signingConfigs.releaseConfig
}
}
compileOptions {
sourceCompatibility JavaVersion.VERSION_1_7
targetCompatibility JavaVersion.VERSION_1_7
}
// 平台定制
productFlavors {
welove {}
}
lintOptions {
ignoreWarnings true // 只报告错误
checkReleaseBuilds true // release构建时issus的严重级为fatal,若发现了致命(fatal)的问题,则中止构建
abortOnError false // 当lint发现错误时停止 gradle构建
}
// 通过查看打包顺序发现打welove的Release包时,第三步就是执行checkWeloveReleaseManifest
// 而且在打Debug包时不会执行checkWeloveReleaseManifest,为了自动执行releaseCheck 增加以下代码
tasks.whenTaskAdded { task ->
if (task.name == 'checkWeloveReleaseManifest') {
task.dependsOn releaseCheck
}
}
}
dependencies {
// 编译libs目录下的所有jar包
compile fileTree(dir: 'libs', include: ['*.jar'])
compile 'com.android.support:support-v4:22.0.0'
compile 'com.android.support:appcompat-v7:22.0.0'
compile 'com.android.support:recyclerview-v7:22.0.0'
compile 'com.android.support:multidex:1.0.1'
compile('com.crashlytics.sdk.android:crashlytics:2.5.5@aar') {
transitive = true
}
}
import java.util.regex.Pattern
task releaseCheck <<{
// 检查 baidu api key 是不是有效值
def baiduApiFile = file("$workspace/tools/WeloveConstants.java")
def baiduApiFilePattern = Pattern.compile("\\s*public static final String BAIDU_MAP_API_KEY = ResourceUtil.getStr\\(R.string.(.*)\\)")
def baiduApiFileMatcher = baiduApiFilePattern.matcher(baiduApiFile.getText())
if (baiduApiFileMatcher.find()){
def baiduApiUrl = baiduApiFileMatcher.group(1)
if (!baiduApiUrl.equals("str_baidu_map_api_key")){
throw new GradleException(baiduApiFile.toString() + " Contain Invalid Baidu API Key ")
}
}else {
throw new GradleException(baiduApiFile.toString() + " Contain Invalid Baidu API Key ")
}
}
因为使用crashlytics,所以根目录的build.gradle文件也贴出来:
buildscript {
repositories {
jcenter()
maven { url 'https://maven.fabric.io/public' }
}
dependencies {
classpath 'com.android.tools.build:gradle:1.5.0'
classpath 'io.fabric.tools:gradle:1.+'
}
}
allprojects {
repositories {
jcenter()
maven { url 'https://maven.fabric.io/public' }
}
}