文章目錄
- Gradle基礎
-
-
- Gradle環境配置
- Android Gradle建構生命周期
- Gradle Wrapper
- Groovy基礎
-
- 字元和字元串
- 集合,List集合,Map集合
- 閉包
- 自定義屬性
- 腳本即代碼,代碼也是腳本
- Gradle插件
-
- 二進制插件
- 應用腳本插件
- 第三方插件
-
- Android Gradle配置
-
-
- 預設配置
- signingConfigs 簽名資訊配置
- buildTypes 建構類型
- sourceSets
- packagingOptions
- 批量修改生成的apk檔案名
- 動态生成版本資訊
- 從git的tag中擷取版本号
-
- 動态擷取版本名稱
- 動态擷取版本号
- 隐藏簽名檔案資訊
- 動态配置AndroidManifest檔案
- 自定義BuildConfig
- 動态添加自定義的資源
- Java編譯選項
- DEX選項配置
- lintOptions
-
Gradle基礎
Gradle環境配置
下載下傳gradle解壓後找到目錄,例如:D:\android\gradle\gradle-4.4\bin。把位址配置到環境變量path中
Android Gradle建構生命周期
- Initialization:初始化階段,會執行項目根目錄下的settings.gradle檔案,來分析哪些項目參與建構
-
Configuration:配置階段,配置階段會去加載所有參與建構的項目的build.gradle檔案,
會将每build.gradle檔案執行個體化為一個Gradle的project對象
- Execution:執行階段,開始打包
Gradle Wrapper
- Gradle的一層包裝,便于開發中統一建構版本。當啟動Gradle時,Wrapper會檢查Gradle有沒有被下載下傳關聯。 在 Wrapper中包含gradle-wrapper.jar和gradle-wrapper.properties。
- gradlew和gradlew.bat分别是Linux和Windows下的可執行腳本。Gradle-wrapper.jar是具體業務邏輯包,gradlwe最終還是使用Java執行的這個jar。
- gradle-wrapper.properties中的配置:distributionBase 下載下傳的gradle壓縮包解壓後存儲主路徑;distributionPath 相對于distributionBase的解壓後的gradle壓縮包的路徑; zipStoreBase 同distributionBase ,隻不過是存放zip壓縮包的; zipStorePath 同distributionPath ,隻不過是存放zip壓縮包的; distributionUrl Gradle發行版本壓縮包的下載下傳位址。
Groovy基礎
Groovy是基于JVM虛拟機的一種動态語言, 它的文法和Java非常相似,是一種靈活的動态腳本語言。每個Gradle的build腳本都是一個Groovy腳本。Groovy是弱類型語言,文法更加靈活,不用像Java中的類以及資料類型需要強制轉換,如下示例:
字元和字元串
task hello << {
def name='a‘
println '單引号:${name}‘
println "雙引号:${name}"
}
//輸出結果
單引号:${name}
雙引号:a
- 不管是單引号還是雙引号都是String類型。
- 單引号沒有運算能力。
集合,List集合,Map集合
//List集合
task printList << {
def numList=[1,2,3,4,5,6]
println numList[1]//通路第二個元
println numList[-1]//通路最後一個元素
println numList[-2]//通路倒數第二個元素
println numList[1..3]//通路第二個到第四個元素
}
//Map集合
task printMap << {
def map=["width":1080,"height":800]
println map["width"]//通路第二個元素
map.each{
println "Key:${it.key},Value:${it.value}"
}
}
閉包
閉包在很多語言中都有這種概念,在Java8中Lambda表達式也是閉包的形式。另外在javascript,kotlin中都有閉包的概念。如下示例:
定義方法customEach,它隻有一個參數用于接收閉包代碼塊。如果隻有一個參數,就用it代替。
task printClosure << {
//使用我們自定義的閉包
customEach{
println it
}
}
//自定義方法
def customEach(closure){
for(int i in 1..10){
closure(i)
}
}
定義方法eachMap,為閉包傳遞兩個參數,key ,value,多個參數就不能用it了,必須要顯示聲明出來。
task printClosure << {
//使用我們自定義的閉包
eachMap{k , v ->
println "${k} is ${v}"
}
}
//自定義方法
def eachMap(closure){
def map=["name":"張三","age":18]
map.each{
closure(it.key,it.value)
}
}
自定義屬性
//自定義 工具版本号
ext.build_tools_version = "25.0.2"
//引用版本号
buildToolsVersion build_tools_version
腳本即代碼,代碼也是腳本
這段代碼是生成時間,可以用于打封包件名
def buildTime(){
def date=new Date()
def formattedDate=date.format('yyyyMMdd')
return formattedDate
}
Gradle插件
二進制插件
apply plugin:'java’
二進制插件就是實作了org.gradle.api.Plugin接口,需要有plugin id。
應用腳本插件
apply from:'version.gradle’
應用腳本插件就是執行了version.gradle一段腳本代碼。
第三方插件
apply plugin:'com.android.application’
classpath ‘com.android.tools.build:gradle:1.5.0’
第三方釋出的作為jar的二進制插件。
Android Gradle配置
預設配置
compileSdkVersion 28
buildToolsVersion '25.0.1'
defaultConfig {
applicationId "com.sort.demo"
minSdkVersion 15
targetSdkVersion 28
versionCode 1
versionName "1.0"
}
- applicationld:用于指定生成app的包名。
- minSdkVersion:最低支援的sdk版本。
- targetSdkVersion:用于配置基于哪個Android Sdk開發。
- compileSdkVersion:目前app編譯版本。
- versionCode:用于配置App内部版本号,通常用于版本更新。
- versionName:用于配置App的版本名稱,是讓使用者知道目前的版本。
- resConfigs:過濾語言
signingConfigs 簽名資訊配置
signingConfigs{
debug{
storeFile file("debug.keystore")
storePassword "password"
keyAlias "test"
keyPassword "password"
}
release{
storeFile file(“release.keystore”) //簽名證書檔案
storePassword “password” //簽名證書檔案密碼
keyAlias “test” //簽名證書秘鑰别名
keyPassword “password” //簽名證書秘鑰密碼
}
}
signingConfigs 需要寫在defaultConfig 和buildTypes 前面,不然會編譯報錯。預設情況下debug模式簽名已經配置好了,在$HOME/.android/debug.keystore。
buildTypes 建構類型
buildTypes {
debug {
minifyEnabled false
proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'
signingConfig signingConfigs.debug
}
release {
minifyEnabled false
proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'
signingConfig signingConfigs.release
}
}
buildTypes的屬性:
- applicationIdSuffix:應用id字尾
- versionNameSuffix:版本名稱字尾
- minifyEnabled:是否混淆
- proguardFiles:混淆檔案
- singningConfig:簽名配置
- manifestPlaceholders:清單占位符
- shrinkResources:是否去除未使用的資源
- zipAlignEnable:是否使用zipalign工具壓縮
- multiDexEnabled:是否拆分成多個Dex
- multiDexKeepFile:指定文本檔案編譯進主Dex檔案中
- multiDexKeepProguard:指定混淆檔案編譯進主Dex檔案中
sourceSets
每一個BuildType都會生成一個SourceSet,預設位置為src//。一個SourceSet包含源代碼,資源檔案等資訊。針對不同的BuildType,可以單獨為其指定Java源代碼,res資源等。jniLibs目錄下放置的是so庫檔案,直接放在目錄下即可。如果so是在libs中,那就需要單獨配置了,如下jniLibs.srcDirs=[‘libs’]。
sourceSets {
main {
res.srcDirs =
[
'src/main/res/google',
'src/main/res/baidu'
]
jniLibs.srcDirs = ['libs']
}
}
開發中我們會引用到aar這樣的架包, 如果aar中的libs存在架包,也需要單獨配置,配置如下:
repositories {
flatDir {
dirs 'libs'
}
}
使用abiFilters 過濾,其實這個可以不設定,這樣編譯時,就會将項目裡所有依賴資源包裡的so庫都打到最終的apk裡。但是有些平台,我們是不需要支援的,如果不删除的話,apk就臃腫了。如果那些so庫是我們自己編譯出來的,那可以直接在工程中删除對應so檔案,但是如果是第三方提供的,就不好删除了,是以就需要使用abiFilters來過濾了。
defaultConfig {
ndk {
abiFilters 'armeabi','armeabi-v7a','arm64-v8a'
}
}
packagingOptions
用于解決檔案沖突問題
packagingOptions {
exclude ’META-INF/LICENSE.txt‘
exclude ’META-INF/LICENSE‘
}
用于解決項目中的jar沖突
compile ('com.wdullaer:materialdatetimepicker:3.2.2') {
exclude group: 'com.android.support', module: 'support-v4'
exclude group: 'com.android.support', module: 'design'
}
簡寫方式
compile() { dep ->
['support-v4', 'support-v13', 'design'].each{ module -> dep.exclude module: module }
}
批量修改生成的apk檔案名
applicationVariants.all { variant ->
variant.outputs.all { output -> // AS 3.0之後版本 each 改為 all
def fileName = "${buildTime()}_release.apk"
def outFile = output.outputFile
if (outFile != null && outFile.name.endsWith('.apk')) {
outputFileName = fileName// AS 3.0之後版本 output.outputFile 改為 outputFileName
}
}
}
動态生成版本資訊
在開發中,一個工程中有多個module,每個module都有build.gradle配置,這些配置中的版本資訊可以統一管理和維護。如下示例:
建立config.gradle内容如下
ext {
android = [
compileSdkVersion: 22,
minSdkVersion : 19,
targetSdkVersion : 22,
buildToolsVersion: '25.0.0',
versionCode : 1,
versionName : "1.0.1",
]
dependencies = [
supportV4: 'com.android.support:support-v4:22.2.1',
design : 'com.android.support:design:22.2.0'
]
}
在主工程中引入config.gradle
apply from: "config.gradle"
module中引用配置的内容
android {
compileSdkVersion rootProject.ext.android.compileSdkVersion
buildToolsVersion rootProject.ext.android.buildToolsVersion
defaultConfig {
applicationId "com.example.demo"
minSdkVersion rootProject.ext.android.minSdkVersion
targetSdkVersion rootProject.ext.android.targetSdkVersion
versionCode rootProject.ext.android.versionCode
versionName rootProject.ext.android.versionName
}
}
dependencies {
compile fileTree(include: ['*.jar'], dir: 'libs')
compile rootProject.ext.dependencies.supportV4
compile rootProject.ext.dependencies.design
}
從git的tag中擷取版本号
git describe --abbrev=0 --tags //擷取目前tag名
動态擷取版本名稱
defaultConfig {
versionCode appVersionCode
versionName getAppVersionName()
}
從git tag中擷取應用的版本名稱
def getAppVersionName() {
def stdout = new ByteArrayOutputStream()
exec {
commandLine 'cmd','git', 'describe', '--abbrev=0', '--tags'
standardOutput = stdout
}
return stdout.toString()
}
動态擷取版本号
defaultConfig {
versionCode getAppVersionCode()
versionName getAppVersionName()
}
以git tag數量作為版本号
def getAppVersionCode(){
def stdout=new ByteArrayOutputStream()
exec {
commandLine 'cmd','git','tag','--list'
standardOutput = stdout
}
return split("\n").size()
}
隐藏簽名檔案資訊
在團隊開發中如果想隐藏簽名資訊,把正式包的時候不受影響。把資訊存到主機環境變量中,既做到隐藏也能正常打包。
signingConfigs {
def appStoreFile=System.getenv("STORE_FILE")
def appStorePassword=System.getenv("STORE_PASSWORD")
def appKeyAlias=System.getenv("KEY_ALIAS")
def appKeyPassword=System.getenv("KEY_PASSWORD")
//當不能從環境變量裡擷取到簽名資訊的時候,就使用項目中帶debug簽名
if(!appStoreFile||!appStorePassword||!appKeyAlias||!appKeyPassword){
appStoreFile="debug.keystore"
appStorePassword="android"
appKeyAlias="androiddebugkey"
appKeyPassword="android"
}
release{
storeFile (appStoreFile)
storePassword appStorePassword
keyAlias appKeyAlias
keyPassword appKeyPassword
}
}
動态配置AndroidManifest檔案
在我們做管道統計的時候一般會這樣做,在打包的時候會把${UMENG_CHANNEL}替換成管道名。
<meta-data android:value="${UMENG_CHANNEL}" android:name="UMENG_CHANNEL"/>
根據管道名來表示管道号,這樣在管道統計的時候就能統計不同的管道。
productFlavors{
google{
manifestPlaceholders.put("UMENG_CHANNEL","google")
}
baidu{
manifestPlaceholders.put("UMENG_CHANNEL","baidu")
}
}
簡寫方式
productFlavors {
google { }
baidu { }
productFlavors.all { flavor ->
manifestPlaceholders.put("UMENG_CHANNEL", name)
}
}
自定義BuildConfig
比如:不同的管道想實作打開不同的網頁,可以這麼做。
productFlavors {
google {
buildConfigField 'String','WEB_URL', ' http://www.google.com '
}
baidu {
buildConfigField 'String','WEB_URL', ' http://www.baidu.com '
}
}
比如:測試包用測試環境,正式包用正式環境。
buildTypes {
debug{
buildConfigField ' String ', ' WEB_URL ', ' http://www.ceshi.com '
}
release {
buildConfigField 'String','WEB_URL', ' http://www.zhengshi.com '
}
}
比如:測試包開啟日志,正式包關閉日志
buildTypes {
debug{
buildConfigField 'bool', 'isDebug', 'true'
}
release {
buildConfigField 'bool', 'isDebug', 'false'
}
}
動态添加自定義的資源
res/values中的資源,不光可以在res/values中使用xml的方式定義,還可以在gradle中定義。這樣不同的管道引用的string是不同的。還可以使用id,bool,dimen,integer,color等類型定義value資源。在BuildType中也可使用。
productFlavors {
google {
resValue 'string','channel_tips','google管道歡迎你'
}
baidu {
resValue 'string','channel_tips','baidu管道歡迎你'
}
}
Java編譯選項
compileOptions {
encoding=‘utf-8’//指定檔案編碼格式
sourceCompatibility=JavaVersion.VERSION_1_8 //源代碼編譯級别
targetCompatibility=JavaVersion.VERSION_1_8//生成Java位元組碼的版本
}
DEX選項配置
在apk打包的時候被dx指令優化成Android虛拟機可執行的DEX檔案。預設情況下被dx配置設定記憶體是一個G,也就是1024MB。
dexOptions {
javaMaxHeapSize ‘4g‘ //執行dx最大堆記憶體
jumboMode true //忽略方法數限制的檢查,apk無法再低版本的裝置上面安裝
threadCount 2 //dx使用的線程數
}
lintOptions
程式在編譯的時候會檢查lint,有任何錯誤提示會停止build,我們可以關閉這個開關。
lintOptions {
abortOnError false //即使報錯也不會停止打包
checkReleaseBuilds false //打包release版本的時候進行檢測
}