Android多管道(平台)打包
- Android多管道平台打包
- 一認識Gradle
- 二多管道打包配置
- 配置多管道
- 自定義管道Apk名字
- 不同管道使用不同的簽名
- 打包
- 三進階開發配置
- 配置不同的資源
- 配置不同的代碼
- 版本号管理
前言:
開發中難免會遇到這樣的兩個需求:
- 國内有n個Android市場,根據不同市場打包出管道名不同的APK來統計下載下傳量;
- 同一個APK要給自己公司旗下不同的代理商使用,功能基本相同,但是圖示等資源有較大差異,又或者簽名也不同、伺服器位址不同等等;
遇到這樣的需求怎麼做呢,難道要建立一個或者多個工程嗎,或者在同一個工程上修改再一次次的打包嗎?直覺告訴我們肯定有更簡便的方法的,而且Android Studio和Gradle這麼智能,是以我們就來一步步探究下。
一、認識Gradle
Gradle是一個集合了Apache Ant和Apache Maven概念的項目自動化建構工具。它使用一種基于Groovy的特定領域語言(DSL)來聲明項目設定。
Gradle配置檔案在項目中展示如下:

build.gradle(Project: xxx):代表項目的基礎配置檔案。
build.gradle(Module: xxx):表示該子產品的配置檔案,也是項目最主要的配置檔案。
setting.gradle(Project Settings):全局的項目配置檔案,裡面主要聲明一些需要加入gradle的Module。
接下來我們主要看下build.gradle(Module: xxx)檔案:
apply plugin: 'com.android.application'
android {
// 編譯SDK的版本
compileSdkVersion
// buildTools版本
buildToolsVersion "25.0.2"
// 預設的配置
defaultConfig {
// 應用的包名
applicationId "com.cooloongwu.multichannel"
minSdkVersion
targetSdkVersion
versionCode
versionName "1.0"
testInstrumentationRunner "android.support.test.runner.AndroidJUnitRunner"
}
buildTypes {
release {
// 是否進行代碼混淆
minifyEnabled false
// 混淆檔案的位置
proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'
}
}
}
// 依賴項目
dependencies {
compile fileTree(dir: 'libs', include: ['*.jar'])
androidTestCompile('com.android.support.test.espresso:espresso-core:2.2.2', {
exclude group: 'com.android.support', module: 'support-annotations'
})
compile 'com.android.support:appcompat-v7:25.3.1'
compile 'com.android.support.constraint:constraint-layout:1.0.2'
testCompile 'junit:junit:4.12'
}
項目建好後,基本配置就這麼多,下面我們一步步增加内容來實作多管道(平台)的打包過程。
二、多管道打包配置
1. 配置多管道
在android标簽中配置productFlavors,比如我的是天氣應用,要給三個平台使用day,night,cold(沒啥别的意思就是這麼整的),那麼分别進行如下配置,如果不同管道的包名不同的話可以添加applicationId進行修改,通過manifestPlaceholders進行管道名的配置。然後在AndroidManifest.xml的application标簽中配置meta-data即可。
//多管道打包配置,signingConfig可配置不同簽名,在AndroidManifest.xml的application标簽中配置meta-data即可,如下所示:
//<meta-data
//android:name="CHANNEL"
//android:value="${CHANNEL_VALUE}" />
productFlavors {
day {
applicationId = "com.cooloongwu.multichannel.day"
manifestPlaceholders = [
CHANNEL_VALUE: "day"
]
//signingConfig signingConfigs.day
}
night {
applicationId = "com.cooloongwu.multichannel.night"
manifestPlaceholders = [
CHANNEL_VALUE: "night"
]
//signingConfig signingConfigs.night
}
cold {
applicationId = "com.cooloongwu.multichannel.cold"
manifestPlaceholders = [
CHANNEL_VALUE: "cold"
]
//signingConfig signingConfigs.cold
}
上面是直接在gradle中配置的,當然也可以在File→Profile Structure中配置,如下圖所示:
配置好後點選Sync Project with Gradle Files同步項目,然後在Android Studio左下角的Build Variants中就多了你配置的管道,如下所示:
當你選中其中一個比如coldDebug時,那麼你點選“Run App”隻會打包并運作這麼一個coldDebug版本的,要想所有的一次性打包出來的話,可以見下文“打包”。
2. 自定義管道Apk名字
根據不同的管道名生成帶管道名字首的Apk友善上傳到不同的市場,需在android标簽中配置applicationVariants.all,代碼如下:
//自定義打包釋出apk名字的格式:xxx_multichannel_1.2.1.apk
applicationVariants.all { variant ->
variant.outputs.each { output ->
def outputFile = output.outputFile
if (variant.buildType.name == 'release') {
def fileName = "${variant.productFlavors[0].name}_multichannel_${defaultConfig.versionName}.apk"
output.outputFile = new File(outputFile.parent, fileName)
}
}
}
3. 不同管道使用不同的簽名
在android标簽中配置signingConfigs,代碼如下:
//多管道簽名配置
signingConfigs {
// day簽名
day{
keyAlias day //别名
keyPassword 'xxx'//别名密碼
storeFile file('D:/Key/day/ day.jks')//簽名檔案路徑
storePassword 'xxx'//簽名密碼
}
//night簽名
night{
keyAlias night//别名
keyPassword 'xxx'//别名密碼
storeFile file('D:/Key/night/night.jks')//簽名檔案路徑
storePassword 'xxx'//簽名密碼
}
//cold簽名
cold{
keyAlias cold//别名
keyPassword 'xxx'//别名密碼
storeFile file('D:/Key/cold/cold.jks')//簽名檔案路徑
storePassword 'xxx'//簽名密碼
}
}
然後在productFlavors标簽中各個管道下,添加相應的簽名配置即可,例如:signingConfig signingConfigs.day。
4. 打包
按照上述配置配置完後,點選右側欄中的Gradle,然後依次展開MultiChannel→:app→Tasks→build,可以看到有下方這些選項了:
assemble以及其他各種assembleXXX顧名思義就是打包出相應的程式了,我們輕按兩下assembleRelease(打包所有管道的Release版本),打包完後可以在MultiChannel\app\build\outputs\apk檔案夾下看到相應的三個APK檔案,并且檔案名也自動生成好了:
三、進階開發配置
1. 配置不同的資源
如果每個APK的應用名稱需要定義的話,那麼可以在productFlavors下每個管道标簽下配置:resValue “string”, “app_name”, ” XXX”。如果在這裡配置了,那麼在strings.xml中就不再需要配置了,否則會提示沖突并報錯。
也可以在app→src下建立相應的檔案夾,比如我這裡建立了day檔案夾,檔案夾中内容同main檔案夾類似,表示day這個管道的應用會使用這個檔案夾下的資源檔案等,在strings.xml中配置不同的應用名稱即可。
同理應用圖示等圖檔各種資源也是這樣的用法,如下圖所示:
2. 配置不同的代碼
這裡要說的是類似微信登入、分享等的回調機制。這種回調機制需要固定的“包名+類名”來實作。來看下微信分享接受傳回值的要求,如下圖所示:
這時問題就來了,你更改了包名,但是代碼中的這處回調卻沒有,是以要在src下建立相應的目錄及回調類,其中幾點要求:
- 建立的目錄層級要跟你在gradle檔案中productFlavors下定義的applicationId一緻。例如我這裡是day,day的applicationId = “com.cooloongwu.multichannel.day”,一定要注意層級,千萬不可出錯;
2.當你複制WXEntryActivity.java檔案到新目錄後,記得要更改包名;
3.在gradle中配置AppID等資訊時,請注意字元串寫法,如下圖所示(當然這些資訊不應直接暴漏出來,我這裡隻作為示範Demo使用);
4.修改AndroidMainfast.xml中的WXEntryActivity節點代碼,不同之處就是多了${APPLICATIONID},這個就是我們在gradle中配置的應用包名,這樣確定能正确根據路徑回調到相應的代碼;
<activity
android:name="${APPLICATIONID}.wxapi.WXEntryActivity"
android:exported="true"/>
3. 版本号管理
每次釋出新版前忘記更改版本号怎麼辦,當然我們可以使用gradle來定義啊!可以參考http://www.jianshu.com/p/a1b0bc453319。
下面主要羅列兩種方式吧(還是有缺陷,以後有好的方式再修改吧):
1、直接使用Git送出次數來定義VersionCode
def cmd_code = 'git rev-list HEAD --first-parent --count'
def gitVersionCode = cmd_code.execute().text.trim().toInteger()
2、使用日期和Git送出次數來定義VersionCode和VersionName
//(其實版本名這個東西還是需要靠手動控制,畢竟定版本資訊還是需要靠人來決定,這個隻是保證你每次送出代碼後會更新下編譯的版本号,版本名你可以自己定義,僞代碼在下面了)
ext.majorCode = //主版本号,手動修改
ext.minorCode = //次版本号,手動修改
ext.revisionCode = //修訂版本号,手動修改
//ext.revisionDescriptionCMD = 'git describe --always'
//ext.tempRevisionDescription = revisionDescriptionCMD.execute().getText().trim()
//ext.revisionDescription = (tempRevisionDescription == null || (tempRevisionDescription).size() == 0) ? new Date().format("yyMMddhhss") : (tempRevisionDescription).substring((tempRevisionDescription).size() - 6)
ext.compileCodeCMD = 'git rev-list HEAD --count'
ext.compileCode = compileCodeCMD.execute().getText().trim().toInteger()//編譯版本号,自動修改,按照Git送出次數生成
然後在gradle檔案中定義兩個方法來擷取版本号和版本名稱。
def getVersionCode(boolean isRelease) {
// 正式環境
if (isRelease) {
majorCode * + minorCode * + revisionCode + compileCode
}
// debug環境
else {
//直接傳回一個日期
Integer.parseInt(new Date().format("yyMMddhhss"))
}
}
def getMyVersionName() {
majorCode + "." + minorCode + "." + revisionCode + "." + compileCode
}
最後在自定義打包apk檔案名的代碼中根據release或者debug來判斷使用不同的版本名稱:
//自定義打包釋出apk名字的格式:xxx_multichannel_1.2.1.apk
applicationVariants.all { variant ->
if (variant.buildType.name == 'release') {
variant.mergedFlavor.versionCode = getVersionCode(true)
} else {
variant.mergedFlavor.versionCode = getVersionCode(false)
}
variant.outputs.each { output ->
def outputFile = output.outputFile
if (variant.buildType.name == 'release') {
def fileName = "${variant.productFlavors[0].name}_multichannel_${defaultConfig.versionName}.apk"
output.outputFile = new File(outputFile.parent, fileName)
}
}
}
最後的最後,文章的源代碼在GitHub:MultiChannel