0x00 前言
Android studio默認(rèn)使用Gradle構(gòu)建Android工程。我之前有花一段時(shí)間研究Gradle,在此總結(jié)一下卒落!
Gradle跟ant/maven一樣导绷,是一種依賴管理/自動化構(gòu)建工具钦购。但是跟ant/maven不一樣葵萎,它并沒有使用xml語言羡忘,而是采用了Groovy語言(語法類似于Java),這使得它更加簡潔滨嘱、靈活,更加強(qiáng)大囊扳。
Gradle的安裝略铣减,百度一大堆劣针。
0x01 HelloWorld
先寫個(gè)hello world吧!
task hello << {
println 'Hello world!'
}
執(zhí)行段語句:gradle -q hello
,-q表示不顯示log,相應(yīng)的還可以 -info
,-debug
來查看詳細(xì)的log信息唤冈。
然后輸出的內(nèi)容是:Hello world!
這就是一個(gè)簡單的gradle文件。里面定義了一個(gè)task:
hello
楼雹。執(zhí)行打印任務(wù)。當(dāng)使用gradle命令行執(zhí)行task時(shí),每個(gè)task只會被執(zhí)行一次崎场,所以gradle test test和gradle test命令的執(zhí)行結(jié)果是一模一樣的。
0x02 Gradle Plugin
看了上面的例子,我們知道如何定義一個(gè)任務(wù)燕酷,但是在Android開發(fā)時(shí)我們看到的Gradle并沒有定義task声诸。因?yàn)樵贏ndroid中我們默認(rèn)添加了Gradle的Android插件:apply plugin: 'com.android.application'
泻肯。 com.android.application
中默認(rèn)定義了我們一些常用的task:
build: 完整編譯項(xiàng)目稚铣,包含編譯,測試和創(chuàng)建jar包
clean: 刪除build目錄
assemble: 編譯和創(chuàng)建jar包
check: 編譯和測試代碼
我們在一個(gè)gradle中可以使用多個(gè)插件惕医。正如我們的項(xiàng)目使用crashlytics
進(jìn)行bug收集統(tǒng)計(jì)耕漱,那么我們的項(xiàng)目中還得依賴apply plugin: 'io.fabric'
插件。
0x03 Android Gradle
在Android Studio新建Android項(xiàng)目現(xiàn)在默認(rèn)使用gradle構(gòu)建抬伺。使用gradle構(gòu)建在規(guī)范的目錄結(jié)構(gòu)與以前ADT生成的有很大的不同了螟够。Gradle要求的一些規(guī)范:工程源碼位于$workspace/app/src/main/java,測試代碼位于$workspace/app/src/test/java沛简。所有位于$workspace/app/src/main/res文件都被包含到j(luò)ar包中作為資源文件齐鲤,所有的輸出文件都會放在build目錄里面,jar文件放在$workspace/app/build/libs目錄下椒楣。
詳細(xì)目錄咱們可以通過命令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命令用來排除一些命令的執(zhí)行淆九,比如gradle build -x ext,會在編譯的時(shí)候不執(zhí)行ext任務(wù)毛俏,即使build task依賴ext也不會執(zhí)行炭庙, 但ext所依賴的task如果被其他task依賴是會執(zhí)行的。
dependencies
DSL 元素是標(biāo)準(zhǔn)的 Gradle API 的一部分煌寇,不屬于android 元素內(nèi)焕蹄。
dependencies {
compile files('libs/test.jar')
}
0x04 Gradlew
在項(xiàng)目的根目錄下我們看到有g(shù)radlew/gradlew.bat文件,gradlew代表 gradle wrapper阀溶,意思是gradle的一層包裝腻脏,大家可以理解為在這個(gè)項(xiàng)目本地就封裝了gradle,即gradle wrapper银锻, 在$workspace/gradle/wrapper/gralde-wrapper.properties
文件中聲明了它指向的目錄和版本永品。只要下載成功即可用grdlew wrapper的命令代替全局的gradle命令。第一次執(zhí)行g(shù)radlew *時(shí)系統(tǒng)會下載創(chuàng)建項(xiàng)目時(shí)使用的gradle環(huán)境击纬,時(shí)間會比較長鼎姐。下載完后就可以直接像gradle一樣使用了。
省略寫法:gradle assembleRelease --> gradle aR
gradlew tasks
gradlew tasks --all
可以得到一個(gè)完整的任務(wù)列表,并且看到任務(wù)運(yùn)行之間的依賴關(guān)系炕桨。
0x05 Gradle Build
Gradle編譯過程如圖:
我們可以查看gradle執(zhí)行過程饭尝,比如我們要編譯一個(gè)welove渠道的release包,執(zhí)行gradle aWR >>1.txt
谋作,>>1.txt表示將打印的內(nèi)容重定向到記事本中芋肠,得到的內(nèi)容:
: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
==
放出我們項(xiàng)目工程的gradle遵蚜,刪除部分關(guān)鍵信息帖池,給大家做個(gè)參考,如下:
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 // 進(jìn)行混淆
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 // 只報(bào)告錯(cuò)誤
checkReleaseBuilds true // release構(gòu)建時(shí)issus的嚴(yán)重級為fatal吭净,若發(fā)現(xiàn)了致命(fatal)的問題睡汹,則中止構(gòu)建
abortOnError false // 當(dāng)lint發(fā)現(xiàn)錯(cuò)誤時(shí)停止 gradle構(gòu)建
}
// 通過查看打包順序發(fā)現(xiàn)打welove的Release包時(shí),第三步就是執(zhí)行checkWeloveReleaseManifest
// 而且在打Debug包時(shí)不會執(zhí)行checkWeloveReleaseManifest寂殉,為了自動執(zhí)行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 ")
}
}
因?yàn)槭褂胏rashlytics囚巴,所以根目錄的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' }
}
}