## android 利用 git 信息區(qū)分 apk 版本
keywords:?android,git柬泽,打包
知識點:
1.android?打包的時候附加上?git?version?code
2.productFlavors
android?打包的時候夺欲,生成的?apk?名字都是?app-debug.apk、app-release.apk活逆,即使設(shè)置了?productFlavors铅忿,也只是類似于?app-channel_googleplay-debug.apk?這種的剪决。通過名字,我們只能區(qū)分出是否是?debug?以及對應(yīng)的渠道號檀训。但是柑潦,對應(yīng)的代碼版本信息是體現(xiàn)不出來的。這在個人開發(fā)的時候弊端不是很明顯峻凫,但是在團隊協(xié)作的時候渗鬼,就會麻煩不斷。首先荧琼,如果你的?android?代碼部署在了?jenkins?等代碼集成服務(wù)器上來自動打包譬胎,那么你永遠(yuǎn)只能得到一個叫?app-release.apk?的最新的包。如果你想看看十天之前的某個包命锄,那么你就只能把代碼?check?到那個時間點然后手動打包堰乔。再比如,測試找到了一個?bug脐恩,但是你覺得不應(yīng)該出現(xiàn)這個?bug?或者這個?bug?在你這里不能復(fù)現(xiàn)镐侯,這個時候你最先問的肯定是,“你裝的是最新的包么驶冒?”然而苟翻,因為?apk?的名字不能體現(xiàn)出代碼的版本號韵卤,所以這個事情就麻煩了。最終的結(jié)果肯定是你放下手頭的工作崇猫,老老實實給測試同學(xué)打一個剛出爐的新包進行復(fù)現(xiàn)沈条。根據(jù)“重復(fù)的事情都要交給計算機來做”原則,我們有必要在打包的過程中添加一個合適的版本信息邓尤。
那么我們用什么信息來當(dāng)我們的版本信息呢拍鲤?首先,我們需要明確汞扎,我們想得到的這個版本信息都包括什么特性:
+自增季稳。比如:1,2澈魄,3……?這樣的好處就是景鼠,可以通過名字比較誰比較新。
+代表一個唯一的代碼版本痹扇。(這是顯然的铛漓。)
根據(jù)你使用的代碼管理工具不同,我們選擇的版本信息也不一樣鲫构。如果是?svn浓恶,?那么用?svn?的版本號是不錯的選擇;如果你用的是?git结笨,那么問題就有點麻煩了包晰。git?沒有類似于?svn?版本號的東西。git?倒是可以用?hashcode?來唯一表示一個代碼版本炕吸。但是伐憾,如果從?apk?的名字上只能得到?hashcode,我們基本得不出什么人能理解的有效信息赫模,也就不能達(dá)到我們提高工作效率的目的树肃。那么怎么辦呢?辦法總是有的瀑罗。對于?git胸嘴,他本身的理念就保證了他根本沒有類似于?svn?版本號一樣的東西。那么我們只能去盡量模擬斩祭,在?git?中找到一個每次提交代碼之后就自增的東西劣像。答案就是`git?rev-list?HEAD?--count`。其實就是實現(xiàn)了一個簡單的對提交的?commit?進行計數(shù)的功能停忿。所以驾讲,我們的?apk?的名字估計會變成`app-release-163.apk`蚊伞。好了席赂,到此我們模擬出了一個自增的版本號吮铭。這里留給讀者朋友們一個小小的思考題:在?git?中,這樣的命名方式是否能唯一確定一份代碼副本呢颅停?
我都這么問了谓晌,答案自然是不能的。因為癞揉,我們是通過數(shù)數(shù)的方式來確定的纸肉。那么很有可能兩個分支數(shù)目是相同的。那么從?apk?的名字上來看喊熟,就不能可能定位到一個唯一的提交上去柏肪。怎么辦?我現(xiàn)在想到了三種辦法:
1.附加一個?branch?信息
2.附加一個?hashcode?作為輔助信息芥牌。commit?count?作為一個初步的比較方式烦味,相當(dāng)于一個快捷方式;hashcode?作為一個準(zhǔn)確但是不常用的的比較手段壁拉。在?commit?count?不能區(qū)分的時候谬俄,借助?hashcode?信息。
3.把最后一個?commit?提交時候的時間(比如:2017_03_10_19_34_10)作為附加信息弃理。這樣其實跟?hashcode?差不多溃论。但是,看起來比?hashcode?順眼一些痘昌。
我這里選用的是第三種钥勋。
附上?builde.gradle?核心腳本,供大家復(fù)制控汉。
```
apply?plugin:?'com.android.application'
apply?plugin:?'android-apt'
android?{
compileSdkVersion?25
buildToolsVersion?'25.0.2'
defaultConfig?{
//?...balabala...
}
signingConfigs?{
debug?{
}
release?{
}
}
buildTypes?{
debug?{
}
release?{
}
}
productFlavors?{
channel_default?{}
channel_googleplay?{}
}
productFlavors.all?{
flavor?->?flavor.manifestPlaceholders?=?[UMENG_CHANNEL_ID:?name]
}
sourceSets?{
main?{
java.srcDirs?=?['src/main/java',?'src/main/java-gen']
}
}
lintOptions?{
abortOnError?false
}
}
dependencies?{
//?...balabala...
}
assemble?{}.doLast{
tasks.copyAndRenameDebugApk.execute()
}
/**
*?use?this?in?Run/Debug?Configurations
*/
task?copyAndRenameDebugApk(type:?Copy){
android.productFlavors.all?{
flavor?->
println(flavor.name)
from?'build/outputs/apk/app-'?+?flavor.name?+??'-debug.apk'
from?'build/outputs/apk/app-'?+?flavor.name?+??'-release.apk'
into?'build/outputs/apk'
rename("app-"?+?flavor.name?+?"-debug.apk",?"app-"?+?flavor.name?+?"-debug-"?+?getGitVersionCode()?+?"-"+?getGitCommitDescription()?+?".apk")
rename("app-"?+?flavor.name?+?"-release.apk",?"app-"?+?flavor.name?+?"-release-"?+?getGitVersionCode()?+?"-"+?getGitCommitDescription()?+?".apk")
}
}
/**
*?return?the?number?of?the?count?of?the?commit
*?@return
*/
def?getGitVersionCode()?{
def?cmd?=?'git?rev-list?HEAD?--count'
int?gitVersion?=?cmd.execute().text.trim().toInteger()
println("the?git?version?is?:?"?+?gitVersion)
return?gitVersion
}
/**
*?get?the?short?description?of?the?latest?commit
*?@return
*/
def?getGitCommitDescription()?{
//????def?cmd?=?"git?rev-parse?--short?HEAD"
def?cmd?=?"git?log?-1?--pretty=format:%cd?--date=iso"
def?proc?=?cmd.execute()
String?cmdResult?=?proc.text.trim()
String[]?resultArray?=?cmdResult.replaceAll(":",?"-").split()
String?apkName?=?(resultArray[0]?+?"-"?+?resultArray[1]).replaceAll("-",?"_")
return?apkName
}
```
解釋一下腳本中的內(nèi)容笔诵。
1.利用?productFlavors?用來設(shè)置友盟渠道號。
```
productFlavors?{
channel_default?{}
channel_googleplay?{}
}
```
2.利用?doLast?設(shè)置調(diào)用的時機
```
assemble?{}.doLast{
tasks.copyAndRenameDebugApk.execute()
}
```
3.對于每一個?flavor?產(chǎn)出的?apk姑子,添加上?git?版本相關(guān)的信息乎婿。具體實現(xiàn)的時候,利用了?gradle?中的?copy?task街佑。其中谢翎,from?和?to?都是本目錄內(nèi),不需做修改沐旨,只需虛晃一槍森逮。真正需要關(guān)心的是?rename。在這里磁携,我們小心翼翼地拼接出了我們期望得到的?apk?的名字褒侧。比如:`app-channel_default-release-698-2017_03_10_19_34_10.apk`。
```
/**
*?use?this?in?Run/Debug?Configurations
*/
task?copyAndRenameDebugApk(type:?Copy){
android.productFlavors.all?{
flavor?->
println(flavor.name)
from?'build/outputs/apk/app-'?+?flavor.name?+??'-debug.apk'
from?'build/outputs/apk/app-'?+?flavor.name?+??'-release.apk'
into?'build/outputs/apk'
rename("app-"?+?flavor.name?+?"-debug.apk",?"app-"?+?flavor.name?+?"-debug-"?+?getGitVersionCode()?+?"-"+?getGitCommitDescription()?+?".apk")
rename("app-"?+?flavor.name?+?"-release.apk",?"app-"?+?flavor.name?+?"-release-"?+?getGitVersionCode()?+?"-"+?getGitCommitDescription()?+?".apk")
}
}
```