1. 程式人生 > >談談Android開發中的Gradle那些事之不同BuildType編譯出不同版本號的apk

談談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,從而可以更改我們想要更改的任意兩者的相關配置