Gradle騷操作
Gradle是谷歌欽定的android項(xiàng)目構(gòu)建工具。熟練使用gradle可以實(shí)現(xiàn)很多騷操作,比如多渠道打包,指定打包文件路徑和文件名等,而且實(shí)現(xiàn)方式不止一種。Gradle工具的編程語言叫做 Groovy, Groovy的語法相對寬松,有點(diǎn)類似javascript,怎么寫的人都有,但是最終目標(biāo)可能是一致的。
本文收錄一些Gradle騷操作供大家分享,經(jīng)本人驗(yàn)證可行,會提供完整Demo(Gradle會存在新舊版本兼容問題,運(yùn)行demo不要改動gradle版本配置):
Demo地址:https://github.com/18598925736/EnjoyGradleHank/commits/master
配置buildTypes{ xxx } 自定義"構(gòu)建類型“
...
android {
...
// 配置簽名文件
signingConfigs {
debug {
// 這里會使用默認(rèn)簽名
}
release {
storeFile file("mykey.jks")
storePassword "android"
keyAlias "android"
keyPassword "android"
v2SigningEnabled true
}
}
//level 1: 打包方式,默認(rèn)有debug和release,當(dāng)然可以自己加喜歡的
buildTypes {
//內(nèi)置 debug 和 release,但是我們可以忽略不計(jì)
uat {
// 測試環(huán)境可調(diào)試
debuggable true
signingConfig signingConfigs.release
}
prd {
// 正式環(huán)境 可調(diào)式
debuggable true
signingConfig signingConfigs.release
}
online {
// 正式發(fā)布包 不可調(diào)試
debuggable false
signingConfig signingConfigs.release
}
}
}
dependencies {
...
}
關(guān)鍵點(diǎn):
-
配置buildTypes之前,必須 配signingConfigs,把簽名文件配好,否則,就會出現(xiàn) install Task缺失的情況
image-20200117112348582.png
- buildTypes里面默認(rèn)就有 debug和release,這一點(diǎn)在as里面可以看到,下圖中另外3個是我們自己加的type:

-
通過指定的buildType打包出來的apk,可以讀到 當(dāng)前包的buildType,并且用于app程序內(nèi)部,比如配置app的網(wǎng)絡(luò)環(huán)境設(shè)置(測試環(huán)境和正式環(huán)境),還有 是否支持日志打印(屏蔽所有日志和放開所有日志),獲取的方式為
BuildConfig.BUILD_TYPE:class MainActivity : AppCompatActivity() { override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) setContentView(R.layout.activity_main) tvBuildType.text = BuildConfig.BUILD_TYPE } }
配置productFlavors{ xxx } 自定義"多種打包風(fēng)味“
Demo地址:https://github.com/18598925736/EnjoyGradleHank/commits/master(切換到時(shí)間點(diǎn):78dc2ca)
打包風(fēng)味 flavor 是 谷歌提供的 多渠道打包的官方手段。
app module 的 build.gradle
...
android {
...
//level 2: 通過productFlavors打包配置,實(shí)現(xiàn)多維度風(fēng)味打包
flavorDimensions "zone", "themeColor"
// 定義多風(fēng)味
productFlavors {
/**
* 越南版 每一個風(fēng)味選項(xiàng)都必須指定獨(dú)立的風(fēng)味值
*/
vn {
applicationId "com.global.vn.ftint"
dimension "zone"
manifestPlaceholders = [zone: "vn"]
}
/**
* 國內(nèi)版
*/
cn {
applicationId "com.global.cn.ftint"
dimension "zone"
manifestPlaceholders = [zone: "cn"]
}
/**
* 主題風(fēng)格為紅色
*/
red {
dimension "themeColor"
manifestPlaceholders = [themeColor: "red"]
}
/**
* 主題風(fēng)格為藍(lán)色
*/
blue {
dimension "themeColor"
manifestPlaceholders = [themeColor: "blue"]
}
}
}
dependencies {
...
}
同時(shí)必須與 manifest配合:
app module 的 AndroidManifest.xml
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="com.zhou.enjoygradle2">
<application
...>
...
<meta-data
android:name="ZONE"
android:value="${zone}" />
<meta-data
android:name="themeColor"
android:value="${themeColor}" />
</application>
</manifest>
關(guān)鍵點(diǎn):
多風(fēng)味打包支持多維度,定義維度是在
flavorDimensions "zone", "themeColor"這樣代碼中 , 此處定義了兩個維度:“地區(qū)”,“主題顏色”-
配置 風(fēng)味 productFlavor的時(shí)候必須指定維度:以下代碼就是指定維度為 zone ,維度的值,則是 cn,同時(shí)還指定了包名applicationId.
cn { applicationId "com.global.cn.ftint" dimension "zone" manifestPlaceholders = [zone: "cn"] } 多種維度的笛卡爾積的數(shù)值,就是 打包方式的總個數(shù),比如上方,zone維度2個風(fēng)味,themeColor維度2個風(fēng)味,所以風(fēng)味的總數(shù)是 2x2=4,在as中也能看到對應(yīng)的task,但是,此處的 維度總數(shù) 和 之前的buildType又形成了一個新的笛卡兒積,之前有5個(uat,prd,online,還有自帶的 debug和release ),所以能看到的task一共是 4x5=20

改變apk輸出路徑和文件名
目前查到有三種寫法:
-
調(diào)用
applicationVariants.all{}重寫參數(shù)閉包,支持絕對路徑的寫法... android { ... //level3 applicationVariants.all { variant -> //這個修改輸出的APK路徑 if (variant.buildType.name != "debug") {//防止AS無法安裝debug包(apk) variant.getPackageApplication().outputDirectory = new File(myPackageDir()) } variant.getPackageApplication().outputScope.apkDatas.forEach { apkData -> //這個修改輸出APK的文件名 apkData.outputFileName = "${appNamePrefix()}" + variant.versionName + "_" + "${releaseTime()}" + "_" + variant.flavorName + "_" + variant.buildType.name + "_" + ".apk" } } } def myPackageDir(){ return "C:\\Users\\adminstrator\\Desktop\\myPackage" } def appNamePrefix() { return "HankZhou" } String releaseTime() { Date date = new Date() String dates = date.format("yyyyMMdd_hhmmss", TimeZone.getTimeZone("UTC")) return dates } dependencies { ... }執(zhí)行
gradlew assembleCnBlueOnline之后在 電腦桌面上的 myPackage 文件夾中會存在一個apk:

-
調(diào)用
applicationVariants.all{}重寫參數(shù)閉包,支持相對路徑的寫法... android { ... // 或者,使用這種方式,只支持相對路徑 applicationVariants.all { variant -> String time = new Date().format('yyyyMMdd_HHmmss') String pkgName = "enjoy_${time}_v${defaultConfig.versionName}_${buildType.name}_${flavorName}.apk" outputs.first().outputFileName = "../../../${pkgName}" // 它不能使用絕對路徑,也就是說可以使用項(xiàng)目的相對路徑 } } ... dependencies { ... }執(zhí)行
gradlew assembleCnBLueUat之后,app module的build目錄內(nèi)會出現(xiàn) apk:

-
通過干涉task的執(zhí)行過程
... android { ... } /** * 當(dāng)有序任務(wù)圖中加進(jìn)去一個任務(wù)的時(shí)候,對它進(jìn)行處理,doLast重新定義完成之后的行為 */ tasks.whenTaskAdded { task -> if (task.name.equalsIgnoreCase("assembleCnBlueUat")) { println("=============發(fā)現(xiàn)任務(wù) ${task.name}") // 如果是assembleRelease任務(wù),在最后執(zhí)行導(dǎo)出apk以及mapping目錄到指定目錄 task.doFirst { println("=============doFirst") } task.doLast { outputReleaseFile() } } } void outputReleaseFile() { println("=============outputReleaseFile") android.applicationVariants.all { variant -> // 如果是正式版打包 if (variant.name.contains("cnBlueUat")) { println("=============準(zhǔn)備拷貝${variant.name}") File outputPath = new File(myPackageDir()) println("==========把文件${variant.outputs[0].outputFile} \n 拷貝到 目標(biāo)位置:$outputPath") outputPath.mkdir() // 但是很明顯,這里的copy并沒有成功 copy {// 拷貝apk文件 println("========開始拷貝...") from variant.outputs[0].outputFile into outputPath // 重命名導(dǎo)出名稱 rename { appNamePrefix() + variant.name + '_' + android.defaultConfig.versionName + '-' + releaseTime() + ".apk" } } } } } def myPackageDir(){ return "C:\\Users\\adminstrator\\Desktop\\myPackage" } def appNamePrefix() { return "HankZhou" } String releaseTime() { Date date = new Date() String dates = date.format("yyyyMMdd_hhmmss", TimeZone.getTimeZone("UTC")) return dates } dependencies { ... }執(zhí)行
gradlew assembleCnBlueUat之后,電腦桌面上會出現(xiàn) mypackage目錄,內(nèi)部有一個apkimage-20200117150006820.png
方式對比
- 調(diào)用
applicationVariants.all{}重寫參數(shù)閉包的方式,修改輸出的絕對路徑,或者相對路徑,分別對應(yīng)兩個api:- 相對 outputs.first().outputFileName
- 絕對 variant.getPackageApplication().outputScope.apkDatas 和 apkData.outputFileName
- 使用 干涉task任務(wù)的方式,和 上面的方式比起來,寫法更加復(fù)雜,但是可控制的精度也更高,比如:它除了可以改變輸出路徑之外,還可以直接指定攔截哪些task,而不是像
applicationVariants.all閉包 一樣,一律變更輸出路徑和文件名。
先寫這么多。后續(xù)還有繼續(xù)補(bǔ)上

