更優雅的 Android 發布自動版本號方案

FredrickQ10 8年前發布 | 22K 次閱讀 Git 安卓開發 Android開發 移動開發

來自: http://www.race604.com/android-auto-version/

以前看到一些自動化版本號打包的文章。如果您的項目是用 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 --first-parent --count'
def gitVersion = cmd.execute().text.trim().toInteger()

android {
defaultConfig { versionCode gitVersion } }</pre>

這里關鍵是這一行 git 命令 git rev-list HEAD --first-parent --count ,表示獲取當前分支的 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 } }</pre>

這樣就可以自動抽取 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" }</pre>

這樣的話,上面的版本名就變為了 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()
    }
}

}</pre>

至此,結合 git 和 gradle 我們就實現了自動版本號。

</div>

 本文由用戶 FredrickQ10 自行上傳分享,僅供網友學習交流。所有權歸原作者,若您的權利被侵害,請聯系管理員。
 轉載本站原創文章,請注明出處,并保留原始鏈接、圖片水印。
 本站是一個以用戶分享為主的開源技術平臺,歡迎各類分享!