簡介
以前知道自動化打包,最近分享點東西,根據(jù)官方和一些參考文檔褥琐,做一下簡單的總結(jié)逗堵,簡單做了張圖,知識點文章都會介紹到谦趣,做了個demo,對各功能做個測試。
build生成apk方式
1.android sutidio 菜單欄蛆挫,build-》generate signed apk 隨后可以選擇編譯方式和產(chǎn)品風味
2.在android根目錄下命令行
-
./gradlew assemble{BuildType}
對應編譯方式所有productflavor都會輸出 -
./gradlew assemble{productFlavor}{BuildType}
指定構(gòu)建體輸出文件 -
./gradlew assemble
全部構(gòu)建變體都會輸出。
PS:打產(chǎn)品包用Release方式
-
./gradlew assembleRelease
或者簡寫./gradlew aR
打包所有產(chǎn)品風味的包 -
./gradlew assemblerelease_normalRelease
指定產(chǎn)生release_normal產(chǎn)品風味的包
install方式
1.android sutidio 直接菜單欄的Run綠色按鈕妙黍。左下腳菜單Build Variants悴侵,可以選擇不同的構(gòu)建變體
2.在android目錄下運行命令
./gradlew install{productFlavor}{buildType}
注意:這種方式如果build自增體現(xiàn)在輸出的apk文件名上,可能會報找不到apk文件的錯誤拭嫁。
簽名不在版本控制中
正常情況下會在build.gradle
下面配置簽名文件的位置和密碼可免,但是簽名文件和密碼會在版本控制中做粤,不安全浇借,至少密碼在版本控制中是不安全的。
推薦兩種方式
1.自定義存儲簽名密碼文件怕品,并自定義函數(shù)讀取簽名密碼和簽名文件位置妇垢。
2.在本地~/.gradle/gradle.properties
全局文件中配置簽名密碼和簽名文件位置
自定義讀取簽名密碼文件
單獨建立一個簽名密碼的文件,在工程目錄下新建signingfiles
文件夾肉康,在里面存儲簽名文件和簽名密碼文件
- keystore.properties
- guanmai-release-key.keystore
簽名密碼文件
STORE_FILE=../signingfiles/guanmai-release-key.keystore
KEY_ALIAS=guanmai-key-alias
STORE_PASSWORD=guanmai
KEY_PASSWORD=guanmai
在版本控制.gitignore中將密碼文件或者簽名文件忽略掉,在build.gradle
中讀取簽名密碼文件闯估。
在android{}函數(shù)外部增加設置簽名的函數(shù)
//從../signingfiles/keystore.properties下讀取簽名文件的位置和密碼
//如果沒有相關(guān)文件或者參數(shù)不對將拋出異常
def setSigningProperties(){
def propFile = file('../signingfiles/keystore.properties')
if (propFile.canRead()){
def Properties props = new Properties()
props.load(new FileInputStream(propFile))
if (props!=null && props.containsKey('STORE_FILE') && props.containsKey('STORE_PASSWORD') &&props.containsKey('KEY_ALIAS') && props.containsKey('KEY_PASSWORD')) {
android.signingConfigs.release.storeFile = file(props['STORE_FILE'])
android.signingConfigs.release.storePassword = props['STORE_PASSWORD']
android.signingConfigs.release.keyAlias = props['KEY_ALIAS']
android.signingConfigs.release.keyPassword = props['KEY_PASSWORD']
} else {
println 'signing.properties found but some entries are missing'
throw new Exception("signing.properties found but some entries are missing")
//android.buildTypes.release.signingConfig = null
}
}else {
println 'signing.properties not found'
throw new Exception("signing.properties not found:" + propFile.absolutePath)
//android.buildTypes.release.signingConfig = null
}
}
在signingconfigs
讀取并設置密碼和簽名文件路徑
signingConfigs {
release {
setSigningProperties()
}
}
本地配置簽名密碼
rn官網(wǎng)曾提到的方法http://reactnative.cn/docs/0.42/signed-apk-android.html#content
簽名配置文件放在全局的本地~/.gradle/gradle.properties
中,簽名文件也放到自己的本地~/.gradle/release-key.keystore
吼和。
MYAPP_RELEASE_STORE_FILE=~/.gradle/release-key.keystore
MYAPP_RELEASE_KEY_ALIAS=key-alias
MYAPP_RELEASE_STORE_PASSWORD=pass
MYAPP_RELEASE_KEY_PASSWORD=pass
build.gradle
中配置簽名
signingConfigs {
release {
storeFile file(MYAPP_RELEASE_STORE_FILE)
storePassword MYAPP_RELEASE_STORE_PASSWORD
keyAlias MYAPP_RELEASE_KEY_ALIAS
keyPassword MYAPP_RELEASE_KEY_PASSWORD
}
}
這樣簽名文件與與密碼都不在版本控制中睬愤,尤其是工作中正式app簽名用的都是一個,會很方便纹安。
查看簽名文件
//查看簽名文件的屬性
keytool -list -keystore 簽名文件
//查看 apk 的簽名尤辱,需要提前解壓 apk ,獲取 CERT.RSA(位于解壓目錄下 /META-INF 下)
//以下命令行是在 apk 解壓目錄下執(zhí)行
keytool -printcert -file META-INF/CERT.RSA
版本名命名VersionName
VersionName是一個字符串厢岂,例如 "1.2.0"光督,這個是給人看的版本名,系統(tǒng)并不關(guān)心這個值塔粒,但是合理的版本名结借,對后期的維護和 bug 修復也非常重要。
參考
版本號規(guī)則
https://developer.android.com/studio/publish/versioning.html
主流app版本號
http://www.ifeegoo.com/recommended-mobile-application-version-name-management-specification.html
版本versionName主要采用形式
v1.0.0.buildnum
1.版本號
<major>.<minor>.<point>
- major是主版本號卒茬,當項目在進行了重大修改或局部修正累積較多船老,而導致項目整體發(fā)生全局變化時咖熟,主版本號加 1;
- minor是次版本號,一般在軟件有新功能時增長
- point是修正版本號柳畔,一般在軟件有主要的問題修復后增長
2.buidnum為build次數(shù)
可以采用build次數(shù)記錄自增的方式馍管,參考下面的VersionCode
在文件gradle.properties
文件中build.num變量
如何賦值versionCode
版本號,是一個大于 0 的整數(shù)薪韩,相當于 Build Number确沸,隨著版本的更新,這個必須是遞增的俘陷。大的版本號罗捎,覆蓋更新小的版本號。那么除了手動增加外拉盾,還有其他自增的方案
方案
- 隨著build次數(shù)自增 http://www.race604.com/android-auto-version/
- git commit 次數(shù)自增 http://www.race604.com/android-auto-version/
- 發(fā)布時間轉(zhuǎn)換整數(shù)
build次數(shù)自增
1.要實現(xiàn)這個功能桨菜,首先得有個變量記錄次數(shù),在工程根目錄下的gradle.properties
增加build.num
屬性
build.num=1
2.需要讀取個屬性捉偏,定義讀取這個build.num
的函數(shù)
def getBuildNum() {
def buildFile = file('../gradle.properties')
if (buildFile.exists()) {
def Properties buildPop = new Properties()
buildPop.load(new FileInputStream(buildFile))
def buildNum = buildPop['build_num'].toInteger()
println('Current version code is ' + buildNum.toString())
return buildNum
} else {
throw new GradleException("Could not find " + buildFile.absolutePath)
}
}
3.此外我們還得能夠賦值這個屬性倒得,定義自增函數(shù)
//加一標志,確保一次編譯不管生成幾個apk告私,都只增加一
def add_flage = false
//非'Debug'編譯方式,自增
def addBuildNum() {
def runTasks = gradle.startParameter.taskNames.toString()
println("runtasks!: " + runTasks)
if ((runTasks.contains('Debug'))) {
return
}
def File buildFile = file('../gradle.properties')
if (buildFile.exists()) {
def Properties versionProps = new Properties()
versionProps.load(new FileInputStream(buildFile))
def buildNum = versionProps['build_num'].toInteger()
buildNum++
versionProps['build_num'] = buildNum.toString()
versionProps.store(buildFile.newWriter(), null)
println('Updated build number to ' + buildNum.toString())
} else {
throw new GradleException("Could not find " + buildFile.absolutePath)
}
}
4.在合適條件下build先自增承桥,然后在再給versionName和VesionCode賦值
//所有編譯方式BuildType都會遍歷flavors
productFlavors.all { flavor ->
//buid自增,一次編譯只增一次
if (!add_flage ){
addBuildNum()
add_flage = true;
}
//build自增后 重新賦值 versionname;versionCode
versionName "v1.2.1.${getBuildNum()}"
versionCode getBuildNum()
}
git commit 次數(shù)自增
參考http://www.race604.com/android-auto-version/
發(fā)布時間
通過發(fā)布時間函數(shù)
def releaseTime() {
return new Date().format("yyMMddhhmm", TimeZone.getTimeZone("UTC"))
}
將返回的字符串轉(zhuǎn)換為int值驻粟,作為versionCode,versionCode為整數(shù)有長度限制凶异,忘記了多少蜀撑,自行查看官網(wǎng)。
多應用安裝
https://developer.android.com/studio/build/build-variants.html
實現(xiàn)多應用的安裝應該有的步驟
1.首先要讓編譯有不同產(chǎn)品的編譯剩彬,定義不同產(chǎn)品風味productFlavors
2.有了不同的產(chǎn)品編譯酷麦,生成了apk,要定義不同的文件名喉恋,生成不同的apk沃饶。
3.生成不同名子的apk,如果ApplicationId一樣的話轻黑,也不能同時安裝在一臺機器上糊肤,要在productFlavors
定義不同的ApplicationId。
產(chǎn)品風味productFlavors
氓鄙,生成多個apk
- env 發(fā)布測試環(huán)境
- release_normal 正式發(fā)布正常升級
- company_{公司關(guān)鍵詞} 不同公司版本
- 渠道 不同渠道版本
在構(gòu)建的變體里 可以設置不同的build參數(shù)馆揉,如applicationId、渠道號抖拦、versionName等
注意:android studio 正常運行可以選擇構(gòu)建方式(debug簽名文件跟release不一樣)升酣,可以在左下角的Build Variants 菜單中選擇構(gòu)建方式舷暮,注意點看輸出文件
輸出文件
輸出的文件名可以根據(jù)的自己需要自定義例如
${profix}_${projectName}_${variant.versionName}
在 applicationVariants.all定義輸出文件名
//輸出文件參數(shù)設置
applicationVariants.all { variant ->
variant.outputs.each { output ->
def outputFile = output.outputFile
//只定義release編譯方式的apk文件名
if (outputFile != null && outputFile.name.endsWith('.apk') && 'release'.equals(variant.buildType.name)) {
def flavor = variant.productFlavors[0].name;
//不同的falvor,定義不同的輸出的apk文件名前綴
def profix
if (flavor.equals("release_normal")){
profix = "gm"
} else if (flavor.equals('env')){
profix = "test_gm"
}else if (flavor.contains("company_")){
profix = flavor.replace("company_","");
}else {
profix = "gm_" + flavor
}
//定義輸出apk文件名
def fileName = "${profix}_${projectName}_${variant.versionName}.apk".toLowerCase()
output.outputFile = new File(outputFile.parent, fileName)
}
}
}
一機多應用安裝
根據(jù)不同的產(chǎn)品風味ProductFlavors
噩茄,定義不同applicationId
//以company開頭 加入不同公司版
//在這里我可以更改包名 app在手機中可以和其他flavor編譯的app共存
company_xnv{
applicationId "com.versiontest.xnv"
manifestPlaceholders = [PGYER_APPID_VALUE: "other PGYER_APPID_VALUE"]
}
不同編譯擁有不同資源文件
在 main 的同級目錄下創(chuàng)建以productFlavors或者buildTpye名命名的文件夾下面,然后創(chuàng)建資源文件,然后打包的時候 gradle 就會自己替換或者合并資源巢墅。
例如诸狭, App 的默認 string.xml 路徑為main\res\value\string.xml
,那么 相應的構(gòu)建變體路徑 env\res\value\string.xml
君纫,打包 env 構(gòu)建變體的時候會自動替換當中的字符串驯遇。
在android studio 中右鍵選擇new->android resources directory 彈出對話框 ==source set==選項,選擇相應的構(gòu)建變體蓄髓,以及相應的資源文件類型叉庐,確定就會自動新建相應的目錄了。
main 目錄中:
<resources>
<string name="app_name">GuanmaiDemoGradle</string>
</resources>
env 目錄中
<resources>
<string name="app_name">DemoGradle</string>
</resources>
那么用env構(gòu)建apk会喝,apk的名字就會變成DemoGradle
代碼中不同編譯方式得到不同變量
在實際項目陡叠,我們可能需要通過不同的構(gòu)建變體,得到一些不同的變量肢执,產(chǎn)生不同的業(yè)務邏輯枉阵,例如想打一個測試環(huán)境包,就想得到一個環(huán)境變量预茄,賦值不同url前綴兴溜。
大約三種方式,
1.resValue
2.buildConfigField耻陕,
3.<meta data>方式拙徽,也是經(jīng)常看到渠道方式诗宣,除了渠道外膘怕,很多更改第三方appId等,都可參考召庞。
“resValue”在string.xml中增加變量
1.在資源文件中新增變量岛心。在你的 gradle 文件 buildTypes 或者 productFlavors 下面,如 release 體內(nèi)寫上類似:
resValue "string", "app_name_build", "Test打包Rn"
就相當于在資源文件string.xml多了
<string name="app_name_build">Test打包Rn</string>
代碼中就可以讀取string.xml中的app_name_build
篮灼。
注意: 只能在string.xml中新增加字符對鹉梨,不能改變原有的字符對,或者說不能與原有的字符對重復,會報沖突錯誤穿稳。
“buildConfigField”在BuildConfig中新增變量
在BuildConfig中新增變量存皂。在你的 gradle 文件 buildTypes 或者 productFlavors 下賦值新的變量, 例如賦值字符串ENV變量:
buildConfigField "String", "ENV", "\"development\""
在代碼中便可以引用BuildConfig.ENV
<meta data>
(渠道)
渠道一般都是通過友盟,以友盟為例來說明渠道方式旦袋。
通過在 AndroidManifest.xml 文件中 application 標簽下指定<mate-data>
設置占位符來實現(xiàn)動態(tài)替換屬性值骤菠。
<meta-data android:name="UMENG_CHANNEL" android:value="${UMENG_CHANNEL}" />
在build.gradle中,是渠道的構(gòu)建變體 ==UMENG_CHANNEL== 為其名字,其他為default
if (name.contains("debug") || name.contains("release"))
flavor.manifestPlaceholders = [UMENG_CHANNEL: "default"]
else
flavor.manifestPlaceholders = [UMENG_CHANNEL: name]
讀取application中的<meta data>
private String getMetaValue(String metaName){
ApplicationInfo appInfo = null;
try {
appInfo = this.getReactApplicationContext().getPackageManager()
.getApplicationInfo(this.getReactApplicationContext().getPackageName(),
PackageManager.GET_META_DATA);
} catch (PackageManager.NameNotFoundException e) {
e.printStackTrace();
}
String msg=appInfo.metaData.getString(metaName);
return msg;
}
如果渠道很多疤孕,就參考下美團和其他github上的多渠道打包
http://www.reibang.com/p/76ab2ff11229