Android中Gradle詳細實用指南
Android中Gradle實用指南
Gradle讓Android中的依賴管理、庫管理、渠道管理以及一些動態地編譯配置變得極為方便!!
本文是對Gradle在Android項目中進階使用的知識點整理和簡要講解
目錄
- Android工程中的Gradle
- 常用的Gradle Task
- 動態參數配置
- Project:build.gradle
- 全局屬性配置
- 整體結構和描述
- Module: build.gradle
- defaucltConfig
- signConfigs
- buidlTypes
- sourceSets
- productFlavors
- compileOptions
- lintOptions
- build.gradle圖形化配置 </ul> </li>
- dependencies項目依賴
- 自定義方法def
- Gradle編譯提速優化
- 性能檢測
- 禁用Task達到提速
- AAPT
- Gradle編譯優化 </ul> </li>
- 認識Task
- 自定義Plugin
- build.gradle中直接定義
- 當前工程中定義
- 獨立Module自定義插件 </ul> </li>
- 最后 </ul>
- 工程Project 中的 build.gradle : 工程控制Gradle編譯配置
- 模塊module中的 build.gradle : 控制每個Module的編譯過程
- gradle.properties : gradle動態參數的配置文件
- local.properties : 本地的配置,如:SDK位置
- gradle-wrapper.properties :gradle本地代理,聲明了指向目錄和版本
- distributionUrl : 指定gradle版本不存在時,就從Value的地址中去下載。很多時候,我們只要版本換成我們本地存在的gradle版本就可以了
- settings.gradle : 配置Gradle中的Module管理
常用Gradle Task
~ 表示 gradlew (Mac 使用gradle ,沒有w) - gradlew task -all : 羅列出所有Task ,同時攜帶具體作用和相互關系
-
gradlew assembleDebug : 導出所有渠道測試包
- ~ assembleRelease : 導出所有渠道正式包
- ~ assembleBaiduDebug --stacktrace : 導出指定渠道測試包,同時攜帶異常信息 </ul> </li>
-
~ --stop : 立即停止編譯
- ~ check : 檢查任務
- ~ build : 執行了 check和assemble
- ~ clean : 清除所有中間編譯結果 </ul>
- Manifest 占位符: 可以動態配置Manifest的參數
動態參數配置
在Gradle中動態配置資源參數
我們可以根據各自的需求在不同的領域(如:buildType 的debug, defaultConfig ...)下去動態替換或配置我們項目中所使用到的資源,如 log 開關, 針對不同渠道的對應內容字段,不同版本定義引入的不同值等等
首先說清一點,對于動態資源在 build.gradle 中多個領域中的使用,會遵循一下順序來進行覆蓋:
buildType > productFlavor > defaultConfig > Manifest中的配置 > 依賴的第三方庫的配置 > 任意領域中的默認值(也就是沒有設置值)
在Manifest的Application節點下 //這里以友盟為例 <!-- 友盟統計相關meta-data --> <meta-data android:name="UMENG_APPKEY" android:value="balabalabala" /> <meta-data android:name="UMENG_CHANNEL" android:value="${UMENG_CHANNEL_VALUE}" />
在build.gradle中對參數進行動態配置 productFlavors { baidu { manifestPlaceholders = [UMENG_CHANNEL_VALUE: "baidu"] } }</code></pre>
- gradle.properties 的使用
- 系統屬性:
配置: systemProp.proName=123
使用: System.properties['proName'] - 自定義屬性 :
配置: 有key: ray.proName=123 ,無key : proName = 123
使用: 有key: project。property('proName') , 無key : proName
- 系統屬性:
gradle.properties中的配置
使用系統參數配置
systemProp.keyStore=ray.jks
使用key/value鍵值對配置
ray.keyPassword=123456
屬性配置
mKeyAlias=ray
build.gradle中的使用 //簽名打包 release { //簽名文件所在路徑 storeFile file(System.properties['keyStore']) //簽名密碼 storePassword "111111" //別名 keyAlias mKeyAlias keyPassword project.property('ray.keyPassword') }</code></pre>
- BuildConfig 文件
- 在 app/build/generated/source/buildConfig 文件夾下面
- 使用: buildConfigField "String" , "key" , "\"value\""
配置 buildConfig 中的屬性參數 String : 參數類型(int,boolean...), key : 屬性的名字, value : 屬性的值, `` 為轉義字符
- resValue 動態修改工程資源
- 和buildConfig 類似, resValue("string","key","value")
- 其中 string 表示 會在 app/build/generated/res/resValue/.../generated.xml 中生成對應的String 的 key 和Value, 代碼中可以直接 getResources().getString(R.string.key); 獲取到value
- 注意: 因為Gradle編譯的時候會將腳本配置和string.xml文件中配置進行merge,所以string.xml中已經存在的key在編譯前要刪除,否則會報錯
//下面模擬在不同渠道下修改資源參數 productFlavors{ baidu{ buildConfigField "String" , "productCode" , "\"baidu 1.0\"" resValue("string","productName","baidu") } }
Project : build.gradle
全局屬性配置
-
在project : build.gradle 的 buildscript 中聲明 ext 和自定義屬性,然后在其他module中就可以直接使用這個屬性了
如下:
Project : build.gradle buildscript { //自定義工程使用的屬性 ext { kotlin_version = '1.1.0' compile_version = 25 }
//聲明依賴Android Gradle 插件版本 dependencies { classpath 'com.android.tools.build:gradle:2.3.0' classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:$kotlin_version" }
}
Module : build.gradle 中使用 Android { compileSdkVersion compile_version } dependencies { compile "org.jetbrains.kotlin:kotlin-reflect:$kotlin_version" }</code></pre>
-
新建自己的Gradle屬性文件 text.gradle , 然后在 Project 中引入,最后就可以在其他的module中直接使用了,具體應用可以參考 印象筆記Github
如下:
text.gradle 編寫如下: ext { kotlinVersion = "1.1.0" rxjavaLibVersion = "1.2.0"
dependencies = [ // Rx rxJava: "io.reactivex:rxjava:$rxjavaLibVersion" ]
}
Project : build.gradle 中引入 //就是引入他的相對根目錄路徑 apply from: 'config/text.gradle'
Module : build.gradle 中使用 rootProject.ext.XXXXX
dependencies {
def dependencies=rootProject.ext.dependencies
compile dependencies.rxJava compile "org.jetbrains.kotlin:kotlin-stdlib:$rootProject.ext.kotlinVersion"
}</code></pre>
整體結構與描述
//聲明引入的參數配置文件 apply from: 'config/dependencies.gradle' apply from: 'config/text.gradle'
//編譯配置 buildscript {
//自定義參數 ext { kotlin_version = '1.1.0' compile_version = 25 } //Gradle指定使用jcenter代碼倉庫 repositories { jcenter() } //聲明依賴Android Gradle 插件版本 dependencies { classpath 'com.android.tools.build:gradle:2.3.0' classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:$kotlin_version" }
}
//這里可以為項目整體配置屬性 allprojects{ repositories { jcenter() } }
//任務:每次構建的時候刪除指定目錄 task clean(type: Delete) { delete rootProject.buildDir }</code></pre>
Module : build.gradle
控制每個Module的編譯過程以及具體的參數配置
defaultConfig
基本配置信息
//默認配置 defaultConfig { //包名 applicationId "com.rayhahah.gradledemo" //最低版本 minSdkVersion 19 //目標版本 targetSdkVersion 25 //版本代碼 versionCode getVersinCode() //版本 versionName "1.0" //自動化測試 testInstrumentationRunner "android.support.test.runner.AndroidJUnitRunner"
resValue "int","test","1" }</code></pre>
signConfigs
簽名配置信息
signingConfigs {
//debug模式簽名文件 debug {} //簽名打包 release { //簽名文件所在路徑 storeFile file("ray.jks") //簽名密碼 storePassword "111111" //別名 keyAlias "rayhahah" keyPassword "111111" } //自定義簽名配置 ray{ //和上面的屬性一致,根據個人需求實現不同配置 } }</code></pre>
buildTypes
編譯類型 : 指定編譯不同類型情況下的不同配置信息
這里是列舉了部分屬性和方法
//構建配置 buildTypes { release {
//是否啟用資源優化 minifyEnabled //啟用舍棄無用資源,只有當開啟混淆才能夠啟用 shrinkResources false //指定混淆文件 proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro' //指定我們release包的輸出文件名就是我們的渠道名字 applicationVariants.all { variant -> variant.outputs.each { output -> def outputFile = output.outputFile if (outputFile != null && outputFile.name.endsWith(".apk")) { def fileName = "${variant.productFlavors[0].name}" + ".apk" output.outputFile = new File(outputFile.parent, fileName); } } } } debug { } //繼承
// rayhahah.initWith(debug)
//自定義buildType rayhahah { //指定簽名配置文件 signingConfig signingConfigs.debug //包名增加后綴 applicationIdSuffix ".ray" } }</code></pre>
sourceSets
配置資源邏輯組
- 通過指定資源 文件夾路徑構建自己的工程目錄
指定Android所需要文件夾所在具體路徑
sourceSets { //這樣的配置適用于將Eclipse中的項目結構遷移到AndroidStudio中 main { //指定src資源目標目錄 java.srcDirs = ['src'] //指定asset的目標目錄 assets.srcDirs = ['assets'] //指定res的目標目錄 res.srcDirs = ['res'] //指定依賴C文件的目標目錄 jni.srcDirs = ['jni'] //指定依賴so文件的目標目錄 jniLibs.srcDirs = ['libs'] //指定Manifest的目標文件路徑 manifest.srcFile 'AndroidManifest.xml' } }</code></pre>
- 常用! 給自己的layout分包管理!!
1. 在res下新建文件夾 layouts(其實叫什么都無所謂)
- 然后在 layouts下 新建你要分的包 如: activity,fragment 或按照業務模塊來分
- 在分包內新建Android resource directory -> layout 不要改名字
- 在module:build gradle 如下配置
- 然后將以前的layout文件拷貝到對應分包的layout下就可以使用了
PS:只有在Project目錄才能看到,Android目錄結構是看不到的
sourceSets { main { res.srcDirs = [ 'src/main/res/layouts/activity', 'src/main/res/layouts/fragment', 'src/main/res/layouts', 'src/main/res'
] } }</code></pre>
productFlavors
打包渠道配置信息(仔細看代碼注釋)
//多渠道打包配置 //利用Manifest占位符動態參數配置 productFlavors { baidu { manifestPlaceholders = [UMENG_CHANNEL_VALUE: "baidu"] }
wandoujia { manifestPlaceholders = [UMENG_CHANNEL_VALUE: "wandoujia"] } googleplayer { manifestPlaceholders = [UMENG_CHANNEL_VALUE: "googleplayer"] } //不想每一個都去配置渠道名稱也可以用下面這個函數 //這個函數可以將 Manifest中的占位符替換成 每個渠道的名字 productFlavors.all { flavor -> flavor.manifestPlaceholders = [UMENG_CHANNEL_VALUE: name] } }</code></pre>
compileOptions
//編譯配置項 //主要配置Java編譯版本 compileOptions { sourceCompatibility org.gradle.api.JavaVersion.VERSION_1_8 targetCompatibility org.gradle.api.JavaVersion.VERSION_1_8 }
lintOptions
//lint配置項 lintOptions {
//啟用出錯停止grgradle構建 abortOnError false // true--檢查所有問題點,包含其他默認關閉項 checkAllWarnings true // 關閉指定問題檢查 disable 'TypographyFractions','TypographyQuotes' // 打開指定問題檢查 enable 'RtlHardcoded','RtlCompat', 'RtlEnabled' // 僅檢查指定問題 check 'NewApi', 'InlinedApi' // true--生成HTML報告(帶問題解釋,源碼位置,等) htmlReport true // html報告可選路徑(構建器默認是lint-results.html ) htmlOutput file("lint-report.html") // 忽略指定問題的規則(同關閉檢查) ignore 'TypographyQuotes' }</code></pre>
build.gradle 圖形化配置
AndroidStuido 給我們提供了十分友好地對于Build.gradle 圖形化配置的界面,使用如下:
-
File -> Project Structure -> 選擇需要配置的 Module —> 選擇需要配置的領域
如圖十分直觀:
圖形化界面.png
dependencies 項目依賴
- build.gradle中dependencies中的配置解析
//dependencies : 當前Android Module構建過程中所依賴的所有庫 dependencies { //依賴指定目錄下所有指定后綴的文件 compile fileTree(dir: 'libs', include: ['*.jar']) //測試工具依賴 testCompile 'junit:junit:4.12' //遠程庫的依賴 (當從遠程庫中下載一次過后,就會緩存到本地了) //默認遠程庫配置為 jcenter() compile 'com.android.support:appcompat-v7:25.2.0'
//依賴指定文件(這里依賴的是jar包) compile file('libs/test-1.0.0.jar') //依賴本地項目庫 compile project(':testLibrary') //格式: groupId: com.squareup.retrofit2 // artifactId : retrofit // version: 2.1.0 // SNAPSHOT : 表示依賴 retrofit 及其依賴的所有項目,如果他所依賴的項目在本項目中重復出現依賴,則只依賴retrofit項目中的。 // @aar : 表示只依賴retrofit,不依賴他所依賴的項目 compile ('com.squareup.retrofit2:retrofit:2.1.0-SNAPSHOT@aar') { //強制刷新遠程庫,避免遠程庫刷新,本地未更新 transitive = true //exclude : 單獨去除okhttp3的依賴 exclude module : 'com.squareup.okhttp3:okhttp:3.3.0' }
}</code></pre>
-
so庫依賴
- 在 src/main 目錄下創建 jniLibs ,然后將so文件拷貝進去就可以了
- 當然也可以通過 sourceSet 指定 jniLib 的目標目錄來自定義管理依賴的so文件存放
-
本地Module依賴
- 在 build.gradle 的 dependencies 領域中添加 compile project(':testLibrary')
- 在 setting.gradle 中添加module到 include 中 如: include ':app',':testLibrary'
方法def定義
在Gradle中你可以寫方法供 配置信息動態調用
//自定義函數 def getVersinCode() { // ...... }
Android{ defaultConfig{ versionCode getVersinCode() } }</code></pre>
Gradle編譯提速優化
檢測
-
gradlew build -profile : 編譯工程同時生成編譯性能分析文件,在根目錄 build/reports/profile/profile-xxxx.xxx....html ,通過瀏覽器打開以后
如圖: 我們需要優化的就是 Task Execution
profile.png
- 往下拉,可以看到TaskExcution的詳細參數
TaskExcution.png
可以看到lint耗時最多,然后我們就可以根據自己項目中的具體情況來做優化
禁用Task達到提速
- 根據上面的耗時來對應禁用Task來達到提速的效果
- 在 Project:build.gradle 中的 buildScript 中動態配置編譯時禁用即可, 代碼: gradle.startParameter.excludedTaskNames.add('lint') 就可以實現禁用了,具體需要繼續禁用的可以根據項目輸出的編譯分析文件來作出添加和調整
AAPT
aapt即Android Asset Packaging Tool,在SDK的build-tools目錄下。該工具可以查看,創建, 更新ZIP格式的文檔附件(zip, jar, apk)。也可將資源文件編譯成二進制文件,盡管你可能沒有直接使用過aapt工具,但是build scripts和IDE插件會使用這個工具打包apk文件構成一個Android 應用程序(百度百科)
- 在Debug模式下,我們需要優化AAPT來大量提速我們的編譯(記得在release下改回來)
aaptOtions{ cruncherEnabled = false }
Gradle編譯優化
- 提升Gradle本身編譯速度
- gradle.properties 中配置
//開啟守護線程支持 org.gradle.daemon=true
//開啟并行編譯 org.gradle.parallel=true //按需編譯 org.gradle.configureondemand=true
//手動配置Gradle編譯時內存分配
Default value: -Xmx10248m -XX:MaxPermSize=256m
org.gradle.jvmargs=-Xmx2048m -XX:MaxPermSize=512m -XX:+HeapDumpOnOutOfMemoryError -Dfile.encoding=UTF-8
開啟JNI編譯支持過時API
android.useDeprecatedNdk=true</code></pre>
- build.gradle 中配置
//增加編譯內存到4g dexOptions{ incremental true javaMaxHeapSize "4g" }
認識Task
- Task基本使用:
task testTask << {
println 'testTask << print' // 表示在task最前面來執行的過程 doFirst { println 'testTask do first'} // << 和 doLast表示對當前task追加執行過程,效果是一樣的 doLast{ println 'testTask do last!'}
}
//task之間的依賴 dependsOn //當執行存在依賴的task時,會先執行他的父類也就是依賴目標 task testDependsOn(dependsOn:testTask){ println 'testDependsOn default print ' } //或者 testDependsOn.dependsOn testTask //當執行testDependsOn是 打印順序: testDependsOn default print -> testTask do first -> testTask << print -> testTask do last! //順序總結為: //1.不加doLast和doFirst的最先執行 //2.依賴task優先級高于自己的doFirst和doLast //3.同一個task中的doLast按從上向下順序執行 //4.同一個task中的doFirst按從下到上倒序執行 //5.同一個task的doFirst優先級高于doLast
//顯示聲明類型為Copy, 不聲明默認為defaultTask task testCopy(type : Copy){ //將當前gradle文件從src目錄拷貝到dst目錄 from "src" into "dst" } //每一個特定的Task類型還可以含有特定的Property,比如Copy的from和to等。
//自定義property ext.testProperty = ""
task testExtProperty << { //直接使用自定義的property println testProperty }</code></pre>
- 自定義Task:
//局部自定義Task //直接在build.gradle中自定義Task //但是也只能在當前module中引用 class TestCustomTask extends DefaultTask { //@Optional,表示在配置該Task時,message是可選的。 @Optional String message = 'I am jjx' //@TaskAction表示該Task要執行的動作,即在調用該Task時,hello()方法將被執行 @TaskAction def hello() { println "hello world $message" } }
//hello使用了默認的message值 task hello(type: TestCustomTask)
//重新設置了message的值 task helloOne(type: TestCustomTask) { message = "I am a android developer" }
全局自定義Task 如果需要自定義大量的Task,就要新建一個Gradle文件來統一管理 通過apply來引入使用 //這是插件 apply plugin: 'com.android.application' //這里gradle-quality.gradle就是另外單獨定義了task的gradle apply from: '../build-config/gradle-quality.gradle'</code></pre>
自定義Plugin
自定義Plugin可以讓我們在工程編譯根據需求中自動去完成一些操作
下面就是一個編譯后自動打印當前時間的Plugin
build.gradle中直接定義
與自定義Task十分類似 可以在build.gradle中自定義plugin apply plugin: DateAndTimePlugin
dateAndTime { timeFormat = 'HH:mm:ss.SSS' dateFormat = 'MM/dd/yyyy' }
class DateAndTimePlugin implements Plugin<Project> { //該接口定義了一個apply()方法,在該方法中,我們可以操作Project, //比如向其中加入Task,定義額外的Property等。 void apply(Project project) { //加載Extension project.extensions.create("dateAndTime", DateAndTimePluginExtension)
//使用Extension配置信息 project.task('showTime') << { println "Current time is " + new Date().format(project.dateAndTime.timeFormat) } project.tasks.create('showDate') << { println "Current date is " + new Date().format(project.dateAndTime.dateFormat) } }
} //每個Gradle的Project都維護了一個ExtenionContainer, //我們可以通過project.extentions進行訪問 //比如讀取額外的Property和定義額外的Property等。 //向Project中定義了一個名為dateAndTime的extension //并向其中加入了2個Property,分別為timeFormat和dateFormat class DateAndTimePluginExtension { String timeFormat = "MM/dd/yyyyHH:mm:ss.SSS" String dateFormat = "yyyy-MM-dd" }</code></pre>
工程中定義
其實本質上就是對上面的自定義Plugin結構化拆解
-
Plugin目錄創建
- 在根目錄下創建 buildSrc
- 子目錄結構如下: buildSrc/src/main/groovy/com/ray 和 buildSrc/src/main/resources/META-INF/gradle-plugins
-
創建 buildSrc/build.gradle , 配置如下
apply plugin:'groovy'
dependecies{ compile gradleApi() compile localGroovy() }</code></pre>
-
Plugin 邏輯實現
自定義Plugin的主要實現邏輯
在 buildSrc/src/main/groovy/com/ray 下創建 DateAndTimePlugin
class DateAndTimePlugin implements Plugin<Project> { void apply(Project project) { //加載Extension project.extensions.create("dateAndTime", DateAndTimePluginExtension)
//使用Extension中的配置信息 project.task('showTime') << { println "Current time is " + new Date().format(project.dateAndTime.timeFormat) } project.tasks.create('showDate') << { println "Current date is " + new Date().format(project.dateAndTime.dateFormat) } }
}</code></pre>
-
Extension 實現
Extension 相當于Gradle配置信息(相當于實體類),然后主項目的 build.gradle 通過 Extension 傳遞配置(相當于賦值)
同樣在 buildSrc/src/main/groovy/com/ray 下創建 DateAndTimePluginExtension
//每個Gradle的Project都維護了一個ExtenionContainer, //我們可以通過project.extentions進行訪問 //比如讀取額外的Property和定義額外的Property等。 //向Project中定義了一個名為dateAndTime的extension //并向其中加入了2個Property,分別為timeFormat和dateFormat class DateAndTimePluginExtension { String timeFormat = "MM/dd/yyyyHH:mm:ss.SSS" String dateFormat = "yyyy-MM-dd" }
-
Plugin命名
自定義外部引用時Plugin的名字
在 buildSrc/src/main/resources/META-INF/gradle-plugins 下創建
timePlugin.properties ,內容只有一行代碼 : implementation-class = com.ray.DateAndTimePlugin
-
Plugin的使用
主項目中 apply plugin:'timePlugin'可選:配置 Extension :
timePlugin{ //動態修改和配置Extension屬性 //這里修改了日期格式 timeFormat = 'MM/dd/yyyy' }
獨立Module自定義插件
- 新建plugin目錄結構如圖:
module_plugin目錄.png
- module_name/build.gradle 的修改 如下:
apply plugin: 'groovy' //增加Maven的支持 apply plugin: 'maven'
version = 1.0 group = 'com.ray.plugin' archivesBaseName = 'timeplugin' repositories.mavenCentral()
dependencies { compile gradleApi() groovy localGroovy() }
//將插件部署到repo目錄下 uploadArchives { repositories.mavenDeployer { repository(url: uri('../repo')) } }</code></pre>
- 發布:和其他Module一樣發布到中央庫中
- 使用插件,主項目中配置如下:
apply plugin: 'timePlugin'
buildscript { repositories { maven { url uri('../repo') } } dependencies { classpath group: 'com.ray.plugin', name: 'timePlugin', version: '1.0' } }</code></pre>
最后
以上就是總結的Gradle實用的進階指南,讓我們可以更加隨心所欲地去管理我們的工程。以后如果有一些新的認識或者想法,我也會在這里實時更新的。也算是自己對Gradle認識和學習的總結整理吧。
正確使用Gradle的配置是為了讓我們開發更加便捷、效率更高,千萬不要本末倒置了。
如果哪里理解錯誤,歡迎大家指出糾正
如果文章對你有用的話,請點贊鼓勵一下哈O(∩_∩)O~~
來自:http://www.jianshu.com/p/ed4ef3b96a29
Android工程中的Gradle
下面簡述對我們工程最重要的幾個Gradle文件,后續也會圍繞他們進行詳細講解和補充
(請仔細看代碼中的注釋哈)