談談Android開發中的Gradle那些事之不同BuildType編譯出不同版本號的apk
今天我們要來談談 如何讓不同的BuildType編譯出不同的版本號
- 沒搞錯吧?
- 這有什麼用??
- 為什麼會有這樣的需求???
不想當產品的QA不是一個好RD
RD不只是PM的RD, QA也總能在非常時期提出一些非常獨(坑)特(爹)的需求,而我們只能說沒問題!(: 男人怎麼能說自己不行)
廢話不多說,正式進入今天的主題:
為什麼要編譯出不同的版本號呢?
相信大部分公司為了更好的統計、分析app線上crash率等,都會用到一些第三方crash統計,這裡拿Fabric舉例
Fabric 只能根據app的versionCode值(也就是最終寫到manifest檔案中的versionCode值)來區分app版本。
這裡我為什麼說最終呢,彆著急,耐心看下去。
此時,我們的最(罪 )佳(魁)P(禍)M(首) QA發話了..
那個 ...
考慮到QA在測試的過程中會遇到一些難以復現的crash等,我們需要在測試環境下也統計crash資訊
且 ...
要和線上的區分開來
於是我們的故事就從這裡開始了......
如果說要根據不同的Flavor編譯出不同的版本號,那太easy了
(這裡我們的versionCode是根據versionName按規則轉換而來的,轉換規則不足3位前面補0: 這裡versionName 1.2.1)
productFlavors {
// 測試環境的包
dev {
versionCode 121
}
app {
versionCode 1002001
}
}
O啦,是不是很簡單。
那麼問題來了,這樣的話就得依賴flavor。而事實上我們往往通過flavor來作為渠道的區分。
那這麼一來就會引起衝突:我們既要產生多渠道,又要編譯出不同的VersionCode.
排除上述方式,我的天啊。殺了我吧,搞不了!
別怕。世上無難事,只怕有心人。
在這裡我首先要感謝強大的網際網路,還要感謝人民,以及背後所有默默支援我的朝陽群眾。
經過不屑的努力,終於被我們發現了,快到碗裡來。
首先在主module下的build.gradle中定義一個方法 getAppVersionCode
def getAppVersionCode(shortVersionCode) {
String versionName = rootProject.ext.android.versionName
String code = ""
if (shortVersionCode) {
code = versionName.replaceAll("\\.", "")
} else {
String[] version = versionName.split("\\.");
version.each {
while (it.length() < 3) {
it = "0" + it;
}
code += it;
}
}
println code
return Integer.valueOf(code)
}
這裡我們先說一下rootProject.ext.android.versionName
通常我們為了更好的管理Android版本資訊、常用依賴等,我們將這些基礎配置寫到一個單獨的gradle檔案中作為全域性配置。配置ext,然後在我們的工程根目錄下的build.gradle檔案中做如下配置。這樣就可以在全域性gradle中訪問ext中的資訊了。
apply from: 'config.gradle'
ext {
// 統一配置support包版本
supportLibraryVersion = '23.1.1'
android = [compileSdkVersion : 23,
buildToolsVersion : "23.0.3",
minSdkVersion : 15,
targetSdkVersion : 22,
versionName : "1.3.1",
renderscriptTargetApi: 19,]
}
細心的同學可能會發現這裡只定義了VersionName,而沒有定義VersionCode。
為什麼呢?山人自有妙用!
- 好好說話
因為我們的VersionName是不變的,而VersionCode要根據環境,然後通過呼叫上面定義的getAppVersionCode去賦值
聽到這裡是不是有思路了。- 別想了,你知道在哪裡賦值嗎?
getAppVersionCode方法就是用來根據VersionName生成versionCode值,這裡接收 一個引數,長位還是短位的來得到我們需要的
方法定義好了,該怎麼用呢?
注意了,注意了,重要的事情說三遍:
大招來了!!!
大招來了!!!
大招來了!!!
applicationVariants.all { variant ->
variant.outputs.each { output ->
// 獲取渠道
def flavor = variant.productFlavors[0]
def flavorName
if (flavor == null) {
flavorName = "defaultName"
} else {
flavorName = flavor.name
}
def outputFile = output.outputFile
def fileName
if (outputFile != null && outputFile.name.endsWith('.apk')) {
def isRelease = variant.buildType.name.contains('release');
// 非release為3位code,用於區分fabric線上和dev
// variant.mergedFlavor.versionCode 表示修改打包時versionCode的值
variant.mergedFlavor.versionCode = getAppVersionCode(!isRelease)
fileName =
"${flavorName}-${variant.buildType.name}-v${variant.versionName}(${variant.versionCode}).apk";
println fileName
output.outputFile = new File(outputFile.parent, fileName)
}
}
}
最關鍵的一步,你有看到嗎。
variant.mergedFlavor.versionCode = getAppVersionCode(!isRelease)
沒錯,就是這裡。我們在自定義編譯後的apk重新命名的同時,遍歷我們的輸出檔案,讀取apk,然後訪問variant的buildtype判斷是release,就設定versoncode為長位的,否則為短位的。
- 啊?這麼簡單?
- 沒錯就是這麼簡單。
這裡我們簡單的說一下 Variant
- Variant就是Build Variant,中文含義 “構建變種版本”
- Build Variant = Build Type + Product Flavor
構建變種版本= 構建型別+定製產品 - 我們可以通過Variant訪問到productFlavors,buildType,從而可以更改我們想要更改的任意兩者的相關配置