Android 發布自動版本號方案


以前看到一些自動化版本號打包的文章。如果您的項目是用 Git 管理的,並且恰巧又是使用 Gradle 編譯(應該絕大部分都是這樣的了吧?),本文試圖找到一種更加優雅的自動版本管理方法。

1 背景
我們都知道,Android 應用的版本管理是依賴 AndroidManifest.xml 中的兩個屬性:

android:versionCode:版本號,是一個大於 0 的整數,相當於 Build Number,隨着版本的更新,這個必須是遞增的。大的版本號,覆蓋更新小的版本號;
android:versionName:版本名,是一個字符串,例如 "1.2.0",這個是給人看的版本名,系統並不關心這個值,但是合理的版本名,對后期的維護和 bug 修復也非常重要。
在使用了 Android Studio 或者 Gradle 編譯以后,我們通常是在 build.gradle 里面定義這兩個值,如下:

android {  
    ...
    defaultConfig {
        ...
        versionCode 1
        versionName "1.0"
    }
}
2 自動版本號
在這篇文章中 6 tips to speed up your Gradle build 發現了,可以使用 Git 中 commit 的數量來作為版本號(versionCode)。方案如下:

def cmd = 'git rev-list HEAD --count'  
def gitVersion = cmd.execute().text.trim().toInteger()
 
android {  
  defaultConfig {
    versionCode gitVersion
  }
}
這里關鍵是這一行 git 命令 git rev-list HEAD --count,表示獲取當前分支的 commit 數量。

注:謝謝 @zhangls 提醒。原文直接使用了 6 tips to speed up your Gradle build 中的命令 git rev-list HEAD --first-parent --count,也就是帶了 --first-parent 選項。發現使用此選項並不合理,比如 這篇文章 中提到的,first-parent 可能會出現不穩定的情況,這就可能導致獲得的 commit 數量並不保證是遞增的。

這是一個絕妙的方案。因為在項目開發中,我們的往 Git 庫中提交的 Commit 的數量應該是只增不減的(當然,在極少的情況下有例外),而且對應 Commit 的數量直接對應代碼當前的版本狀態,只要你做了代碼修改,版本號就應該增加。有些解決方案中,每次 Build 就會增加一次版本號,個人感覺並不合適,如果是相同的代碼,發布出去版本號應該保持一致,而不在於你編譯多少次。

另外,有些人可能會擔心,每次版本發布,可能會包含幾百個新的 commit,這樣的話 versionCode 會不會增長太快了,最后導致不夠用了。其實,完全沒有必要擔心,versionCode 是 int 類型,最大值是 2^31-1,也就是 21 億多,Android 源碼中,改動最活躍的 framework/base 所有分支到目前為止也就 20 萬多個 commit,所以完全夠用了。

3 自動版本名
前面通過一條簡單的命令實現了自動化的 versionCode,現在我們看怎么自動化versionName。

在正常的發布流程中,在發布新版本的時候,都會在版本庫中打 tag。一般情況下,tag 名就是版本名,而且也建議這么做,因為如果某個版本出現 bug,也可以正好 checkout 這個 tag 來查看代碼。所以,現在的問題就是怎么自動獲得 git 庫中最新的最新 tag?原來,git 早就提供了命令 git describe,它的功能就是獲取從當期 commit 到距離它最近的 tag 的描述。默認都是 annoted tag,如果要指所有的類型的 tag 的話,就加 --tags參數。

此命令的詳細介紹在這里:git-describe。舉例一個簡單的例子,假如你的當前代碼狀態如下:

--A--B-...-C-->
     |     |
   v1.0  v1.1
執行 git describe 的結果是:v1.1,如果是如下的情況:

--A--B-...-C--D-->
     |     |
   v1.0  v1.1
執行 git describe 的結果是:v1.1-1-gXXXXXX,其中 1 表示當前代碼距離最近的 tag v1.1一個 commit,最新的 commit 的 id 是 XXXXXX。

可見,describe 命令很好的描述了當前的分支的版本狀態,我們可以直接使用這個它的輸出作為版本號。在 build.gradle 中的使用如下:

def cmd = 'git describe --tags'  
def version = cmd.execute().text.trim()
 
android {  
  defaultConfig {
    versionName version
  }
}
這樣就可以自動抽取 git 中的 tag 為版本名了。有些同學可能接受不了這樣版本名字 v1.1-1-gXXXXXX,這里也可以稍微做一些修改,使版本號更好看,如下:

def pattern = "-(\\d+)-g"  
def matcher = version =~ pattern
 
if (matcher) {  
    version = version.substring(0, matcher.start()) + "." + matcher[0][1]
} else {
    version = version + ".0"
}
這樣的話,上面的版本名就變為了 v1.0.0 和 v1.1.1 了。

4 優化
前面的那篇文章中說了,為了盡可能減少 gradle 腳本的運算,提高開發速度,我們可以把這樣的自動版本的計算放到 release 編譯中去。最后的寫法如下:

def gitVersionCode() {  
    def cmd = 'git rev-list HEAD --first-parent --count'
    cmd.execute().text.trim().toInteger()
}
 
def gitVersionTag() {  
    def cmd = 'git describe --tags'
    def version = cmd.execute().text.trim()
 
    def pattern = "-(\\d+)-g"
    def matcher = version =~ pattern
 
    if (matcher) {
        version = version.substring(0, matcher.start()) + "." + matcher[0][1]
    } else {
        version = version + ".0"
    }
 
    return version
}
 
android {  
    compileSdkVersion 23
    buildToolsVersion "23.0.2"
 
    defaultConfig {
        applicationId "com.race604.example"
        minSdkVersion 15
        targetSdkVersion 23
        versionCode 1
        versionName '1.0'
    }
    buildTypes {
        debug {
            // 為了不和 release 版本沖突
            applicationIdSuffix ".debug"
        }
        release {
            minifyEnabled false
            proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'
        }
    }
    applicationVariants.all { variant ->
        if (variant.buildType.name.equals('release')) {
            variant.mergedFlavor.versionCode = gitVersionCode()
            variant.mergedFlavor.versionName = gitVersionTag()
        }
    }
}
至此,結合 git 和 gradle 我們就實現了自動版本號。

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM