文檔概述
關于Android開發(fā)饲趋,除了技術方面需要掌握,還有發(fā)布流程需要了解撤蟆。本文檔就包括以上兩個方面奕塑,主要介紹:
- 使用配置文件配置不同功能的apk
- 使用gradle為Android構建簽名包
- Jenkins集成Android自動化打包
- 使用gradle為Android生成不同配置的簽名包
一、場景描述
在項目開發(fā)中枫疆,我們可能有多個功能有區(qū)別但是整體框架一致的工程爵川。
情景:我們有一app敷鸦,基礎功能包括a,b,c,擴張功能包括d,e,f息楔,其中客戶1需要擴展功能d,f,客戶2需要e,f扒披。
二值依、配置文件簡介
鑒于以上場景,開發(fā)app過程應該怎么做碟案?如果客戶1創(chuàng)建一份工程代碼愿险,客戶2創(chuàng)建一份工程代碼效率就太低了。因此我們需要另辟思路价说,采用配置文件的方式進行開發(fā)辆亏。
配置文件分類
關于配置文件目前有兩種方式:
- apk文件寫注釋
- apk源碼屬性文件
配置方式的區(qū)別
方式1主要適用于輕量的注釋,例如書寫渠道名等一些簡單的注釋鳖目,不用修改源碼扮叨。方式2就比較適合當前的場景,但是缺點在于配置文件在源碼部分领迈,所以修改配置文件就必須修改源碼彻磁。
配置文件的使用
關于方式1的使用,可以: 美團批量打包
基本原理就是在apk文件生成之后狸捅,修改apk文件的部分字段衷蜓,而不影響apk本身簽名驗證,在源碼中根據(jù)apk安裝的位置獲取安裝包文件再讀取其中的字段尘喝。
方式2就是使用屬性文件磁浇。實現(xiàn)方式就是使用java.util.Properties類進行文件加載。
預先在Android工程的main
文件夾下創(chuàng)建assets
文件夾朽褪,里面存放配置文件置吓,客戶1的配置文件命名為:a.properties
鳍贾,b.properties
,里面的內(nèi)容如下:
fun_a=true
fun_b=true
fun_c=true
fun_d=true
fun_e=false
fun_f=true
讀取配置屬性代碼:
public String funX(Context context, String x){
Properties props = new Properties();
try {
props.load(context.getAssets().open("a.properties"));
// props.load(context.getAssets().open("b.properties"));
} catch (IOException e) {
e.printStackTrace();
}
return props.getProperties("fun_" + x);
}
在源碼中根據(jù)對應函數(shù)的配置信息決定是否執(zhí)行對應操作交洗。
對于客戶1和客戶2的不同需求可以選擇性的加載配置文件進行打包操作骑科。
以上就是不同需求的具體操作。但是我們可以發(fā)現(xiàn)构拳,這個操作效率太低咆爽,每次打包都要修改源碼。下面介紹自動化過程置森。
三斗埂、 自動化之gradle打包
配置操作
為了之后可以執(zhí)行自動化操作,簽名必須也要能進行自動化執(zhí)行凫海。
首先放置簽名文件到:項目的根目錄呛凶。
在module
的build.gradle
文件中添加以下內(nèi)容:
apply plugin: 'com.android.application'
def keystorePSW = ''
def keystoreAlias = ''
def keystoreAliasPSW = ''
// default keystore file, PLZ config file path in local.properties
Properties properties = new Properties()
// local.properties file in the root director
properties.load(project.rootProject.file('gradle.properties').newDataInputStream())
keystorePSW = properties.getProperty("keystore.password")
keystoreAlias = properties.getProperty("keystore.alias")
keystoreAliasPSW = properties.getProperty("keystore.alias_password")
android {
...
signingConfigs {
release {
keyAlias keystoreAlias
keyPassword keystoreAliasPSW
storePassword keystorePSW
storeFile file('../xxx.jks') // 簽名文件的位置
}
}
buildTypes {
release {
minifyEnabled false
zipAlignEnabled true
proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'
signingConfig signingConfigs.release
}
}
}
dependencies {
...
}
為了能夠在Jenkins環(huán)境也能使用自動打包操作,在項目的根目錄gradle.properties
文件中添加如下內(nèi)容:
keystore.password = xxx // 密鑰的密碼
keystore.alias = xxx // 密鑰的別稱
keystore.alias_password = xxx // 別稱的密碼
隨后在項目的根目錄下使用gradle即可進行打包行贪。
執(zhí)行命令
打包命令:
gradlew clean // 清除build文件夾
// 二選一
gradlew build // 檢查依賴并編譯打包漾稀,會生成debug和release兩個包
gradlew assesmRelease // 生成release包
執(zhí)行以上命令之后,會在項目的app/build/output/apk/*.apk
生成對應的apk建瘫。
注:Windows操作系統(tǒng)使用gradlew
崭捍,Linux系統(tǒng)使用./gradlew
。
以上啰脚,使用gradle自動打包就以完成殷蛇。下面就是集成Jenkins持續(xù)集成環(huán)境了。
四橄浓、Jenkins集成
創(chuàng)建項目
關于構建介紹可以參考:Jenkins+Gradle實現(xiàn)android開發(fā)持續(xù)集成粒梦、打包
配置信息沒什么特別的。主要是輸出文件路徑:直接填寫app/build/output/app/*.apk
即可荸实。
可能遇到的問題
-
由于Jenkins自動構建匀们,所以對語法要求比較嚴格。如果出現(xiàn)以下錯誤:
FAILURE: Build failed with an exception. * What went wrong: Execution failed for task ':app:lint'. > Lint found errors in the project; aborting build. Fix the issues identified by lint, or add the following to your build script to proceed with errors: ... android { lintOptions { abortOnError false } } ... * Try: Run with --stacktrace option to get the stack trace. Run with --info or --debug option to get more log output.
這個是因為代碼不符合規(guī)范泪勒,lint檢查時報錯昼蛀,因此中斷了整個編譯過程。
只要在當前app的
app/build.gradle
文件內(nèi)增加如下代碼:android{ ... lintOptions{ abortOnError false } ... }
-
安裝Jenkins配置局域網(wǎng)訪問
在mac安裝Jenkins之后圆存,局域網(wǎng)無法訪問叼旋,原因在于使用brew安裝jenkins會避免很多其他安裝方式產(chǎn)生的用戶權限問題,但是會將httpListenAddress默認設置為127.0.0.1沦辙,這樣我們雖然可以在本地用localhost:8080訪問夫植,但是本機和局域網(wǎng)均無法用ip訪問。解決辦法為修改兩個路徑下的plist配置。
~/Library/LaunchAgents/homebrew.mxcl.jenkins.plist /usr/local/opt/jenkins/homebrew.mxcl.jenkins.plist
修改之后重啟Jenkins即可訪問详民。
brew services start jenkins // 開啟服務 brew services stop jenkins // 關閉服務 brew services restart jenkins // 重啟服務
-
更多錯誤查看:
通過以上操作延欠,Android項目就可以使用Jenkins自動集成了。
但是我們第一部分的場景問題還是沒有解決沈跨,如何自動化區(qū)分客戶的版本呢由捎?
五、自動化版本區(qū)分
Android官網(wǎng)介紹了 構建變體
通過配置不同的 productFlavors
我們可以獲取不同版本的apk饿凛。
因此第一部分的需求通過以下操作實現(xiàn)狞玛。
更新buidl.gradle
文件
android {
...
buildTypes {
...
}
productFlavors {
fun_a {
buildConfigField "String", "CONF_NAME", "\"a.properties\""
}
fun_b {
buildConfigField "String", "CONF_NAME", "\"b.properties\""
}
}
}
- 注1:字段解釋參考 GRADLE自定義你的BUILDCONFIG
- 注2:使用
String
屬性時候,值需要使用\"
進行轉(zhuǎn)義
修改讀取配置文件代碼
配置屬性文件不變涧窒,讀取的代碼進行如下修改:
public String funX(Context contex, tString x){
Properties props = new Properties();
try {
props.load(context.getAssets().open(BuildConfig.CONF_NAME));
} catch (IOException e) {
e.printStackTrace();
}
return props.getProperties("fun_" + x);
}
之后使用打包命令就會生成兩個apk安裝包心肪,一個是客戶1定制的功能,一個是客戶2定制的功能纠吴。
輸出路徑不變硬鞍,生成包名:
- app-fun_a-releas.apk
- app-fun_b-releas.apk
這樣,Jenkins一次編譯也就可以獲取到正確的版本戴已。
修改輸出文件的包名
以上操作之后已經(jīng)可以正確獲取目標apk固该,但是并不直觀。如果可以在輸出文件名中添加輸出版本號和打包時間就完美了恭陡。
在項目的build.gradle
文件中蹬音,最后節(jié)點添加:
def releaseTime() {
return new Date().format("yyyyMMdd-HHmm", TimeZone.getTimeZone("GMT+08:00"))
}
android{
applicationVariants.all { variant ->
variant.outputs.each { output ->
def outputFile = output.outputFile
if (variant.buildType.name.equals('release')) {
def fileName = outputFile.name.replace("app-","").replace("release", "v${defaultConfig.versionName}-${releaseTime()}")
output.outputFile = new File(outputFile.parent, fileName)
}
}
}
}
修改之后輸出文件名:
fun_a-v2.0.5-20171115-1655.apk
fun_b-v2.0.5-20171115-1655.apk
以上上煤。
六休玩、小結(jié)
通過使用gradle+Jenkins,可以讓程序員從繁復的打包任務中解放出來劫狠,更多時間去做核心開發(fā)的相關業(yè)務拴疤。
gradle的功能強大到我沒法想象,好好學習独泞,好好鉆研呐矾。
技術,可以解放你懦砂。