簡介
以前知道自動化打包,最近分享點東西,根據官方和一些參考文檔,做一下簡單的總結,簡單做了張圖,知識點文章都會介紹到,做了個demo,對各功能做個測試。
build生成apk方式
1.android sutidio 菜單欄,build-》generate signed apk 隨后可以選擇編譯方式和產品風味
2.在android根目錄下命令行
-
./gradlew assemble{BuildType}
對應編譯方式所有productflavor都會輸出 -
./gradlew assemble{productFlavor}{BuildType}
指定構建體輸出文件 -
./gradlew assemble
全部構建變體都會輸出。
PS:打產品包用Release方式
-
./gradlew assembleRelease
或者簡寫./gradlew aR
打包所有產品風味的包 -
./gradlew assemblerelease_normalRelease
指定產生release_normal產品風味的包
install方式
1.android sutidio 直接菜單欄的Run綠色按鈕。左下腳菜單Build Variants,可以選擇不同的構建變體
2.在android目錄下運行命令
./gradlew install{productFlavor}{buildType}
注意:這種方式如果build自增體現在輸出的apk文件名上,可能會報找不到apk文件的錯誤。
簽名不在版本控制中
正常情況下會在build.gradle
下面配置簽名文件的位置和密碼,但是簽名文件和密碼會在版本控制中,不安全,至少密碼在版本控制中是不安全的。
推薦兩種方式
1.自定義存儲簽名密碼文件,并自定義函數讀取簽名密碼和簽名文件位置。
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{}函數外部增加設置簽名的函數
//從../signingfiles/keystore.properties下讀取簽名文件的位置和密碼
//如果沒有相關文件或者參數不對將拋出異常
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官網曾提到的方法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 解壓目錄下執行
keytool -printcert -file META-INF/CERT.RSA
版本名命名VersionName
VersionName是一個字符串,例如 "1.2.0",這個是給人看的版本名,系統并不關心這個值,但是合理的版本名,對后期的維護和 bug 修復也非常重要。
參考
版本號規則
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是主版本號,當項目在進行了重大修改或局部修正累積較多,而導致項目整體發生全局變化時,主版本號加 1;
- minor是次版本號,一般在軟件有新功能時增長
- point是修正版本號,一般在軟件有主要的問題修復后增長
2.buidnum為build次數
可以采用build次數記錄自增的方式,參考下面的VersionCode
在文件gradle.properties
文件中build.num變量
如何賦值versionCode
版本號,是一個大于 0 的整數,相當于 Build Number,隨著版本的更新,這個必須是遞增的。大的版本號,覆蓋更新小的版本號。那么除了手動增加外,還有其他自增的方案
方案
- 隨著build次數自增 http://www.race604.com/android-auto-version/
- git commit 次數自增 http://www.race604.com/android-auto-version/
- 發布時間轉換整數
build次數自增
1.要實現這個功能,首先得有個變量記錄次數,在工程根目錄下的gradle.properties
增加build.num
屬性
build.num=1
2.需要讀取個屬性,定義讀取這個build.num
的函數
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.此外我們還得能夠賦值這個屬性,定義自增函數
//加一標志,確保一次編譯不管生成幾個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 次數自增
參考http://www.race604.com/android-auto-version/
發布時間
通過發布時間函數
def releaseTime() {
return new Date().format("yyMMddhhmm", TimeZone.getTimeZone("UTC"))
}
將返回的字符串轉換為int值,作為versionCode,versionCode為整數有長度限制,忘記了多少,自行查看官網。
多應用安裝
https://developer.android.com/studio/build/build-variants.html
實現多應用的安裝應該有的步驟
1.首先要讓編譯有不同產品的編譯,定義不同產品風味productFlavors
2.有了不同的產品編譯,生成了apk,要定義不同的文件名,生成不同的apk。
3.生成不同名子的apk,如果ApplicationId一樣的話,也不能同時安裝在一臺機器上,要在productFlavors
定義不同的ApplicationId。
產品風味productFlavors
,生成多個apk
- env 發布測試環境
- release_normal 正式發布正常升級
- company_{公司關鍵詞} 不同公司版本
- 渠道 不同渠道版本
在構建的變體里 可以設置不同的build參數,如applicationId、渠道號、versionName等
注意:android studio 正常運行可以選擇構建方式(debug簽名文件跟release不一樣),可以在左下角的Build Variants 菜單中選擇構建方式,注意點看輸出文件
輸出文件
輸出的文件名可以根據的自己需要自定義例如
${profix}_${projectName}_${variant.versionName}
在 applicationVariants.all定義輸出文件名
//輸出文件參數設置
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)
}
}
}
一機多應用安裝
根據不同的產品風味ProductFlavors
,定義不同applicationId
//以company開頭 加入不同公司版
//在這里我可以更改包名 app在手機中可以和其他flavor編譯的app共存
company_xnv{
applicationId "com.versiontest.xnv"
manifestPlaceholders = [PGYER_APPID_VALUE: "other PGYER_APPID_VALUE"]
}
不同編譯擁有不同資源文件
在 main 的同級目錄下創建以productFlavors或者buildTpye名命名的文件夾,然后創建資源文件,然后打包的時候 gradle 就會自己替換或者合并資源。
例如, App 的默認 string.xml 路徑為main\res\value\string.xml
,那么 相應的構建變體路徑 env\res\value\string.xml
,打包 env 構建變體的時候會自動替換當中的字符串。
在android studio 中右鍵選擇new->android resources directory 彈出對話框 ==source set==選項,選擇相應的構建變體,以及相應的資源文件類型,確定就會自動新建相應的目錄了。
main 目錄中:
<resources>
<string name="app_name">GuanmaiDemoGradle</string>
</resources>
env 目錄中
<resources>
<string name="app_name">DemoGradle</string>
</resources>
那么用env構建apk,apk的名字就會變成DemoGradle
代碼中不同編譯方式得到不同變量
在實際項目,我們可能需要通過不同的構建變體,得到一些不同的變量,產生不同的業務邏輯,例如想打一個測試環境包,就想得到一個環境變量,賦值不同url前綴。
大約三種方式,
1.resValue
2.buildConfigField,
3.<meta data>方式,也是經常看到渠道方式,除了渠道外,很多更改第三方appId等,都可參考。
“resValue”在string.xml中增加變量
1.在資源文件中新增變量。在你的 gradle 文件 buildTypes 或者 productFlavors 下面,如 release 體內寫上類似:
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>
設置占位符來實現動態替換屬性值。
<meta-data android:name="UMENG_CHANNEL" android:value="${UMENG_CHANNEL}" />
在build.gradle中,是渠道的構建變體 ==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.lxweimin.com/p/76ab2ff11229