轉(zhuǎn)載請(qǐng)標(biāo)明原文地址:http://www.reibang.com/p/843055bf6edd
重要更新
- 現(xiàn)在新建變體還需要在 defaultConfig 下加入
flavorDimensions "versionCode"
- 我寫了一個(gè)腳本可以快速的創(chuàng)建變體的sourceSet预烙,項(xiàng)目地址:junerver/flavor_cli
使用方法:將該腳本復(fù)制到項(xiàng)目根目錄下(與app目錄同級(jí))喜每,然后運(yùn)行指令:python3 flavor_variant.py
埂软,后續(xù)按照提示輸入即可蛛淋,因?yàn)槭枪┳约洪_發(fā)快速使用的挟秤,所有必然有bug與一些嚴(yán)格匹配的要求,歡迎提PR较锡!
背景:剛剛接手的項(xiàng)目中包含3個(gè)客戶端app(兩個(gè)eclipse工程月杉、一個(gè)AS工程),同時(shí)這個(gè)項(xiàng)目根據(jù)不同用戶的制定還有兩個(gè)衍生版本钩杰。原來的開發(fā)人員將項(xiàng)目復(fù)制后修改纫塌,在我接手時(shí)一共存在著9個(gè)工程文件。
當(dāng)我看到這個(gè)項(xiàng)目的時(shí)候近乎崩潰讲弄,因?yàn)檫@意味著每修改一個(gè)端的內(nèi)容還要記著同步到其他的兩個(gè)端中措左。查看后發(fā)現(xiàn),衍生版本中大量的文件是重復(fù)的避除,只是部分比如資源文件怎披、后臺(tái)接口地址等是不同的。我便開始思考瓶摆,如何通過一個(gè)Android Studio工程同時(shí)實(shí)現(xiàn)修改這三個(gè)版本凉逛。
于是便想到了曾看過stormzhang寫過的ANDROID STUDIO系列教程六--GRADLE多渠道打包,這篇文章中簡(jiǎn)單描述了如何使用gradle進(jìn)行多渠道打包(使用占位符替換AndroidManifest文件中友盟統(tǒng)計(jì)的UMENG_CHANNEL的值)群井。
這給了我一定的思路状飞,通過查閱資料,確定了可以使用gradle打包出不同變體(不同applicationId书斜、不同資源文件诬辈、不同APP名稱與圖標(biāo)、不同Java文件)荐吉。
長(zhǎng)篇講解可以查看文章末尾的參考閱讀焙糟,本文只介紹如何實(shí)現(xiàn)。
1 配置不同變體的屬性(簽名样屠、applicationId)
由于項(xiàng)目中每個(gè)客戶端都相當(dāng)于存在三個(gè)版本穿撮,使用不同的簽名文件,因此需要首先實(shí)現(xiàn)的就是對(duì)不同的變體配置不同的簽名痪欲!
如上圖所示悦穿,選擇app module,選擇Signing選項(xiàng)卡勤揩,首先配置好了這三個(gè)變體版本的簽名文件。
在Flavors選項(xiàng)卡中秘蛔,新建我們的變體(variant)陨亡,并對(duì)變體進(jìn)行配置傍衡!所有的Flavor都會(huì)復(fù)寫defaultConfig中的屬性,所以可以看到我并沒有填寫其中的一些屬性负蠕。在這里還可以對(duì)不同的變體設(shè)置不同的applicationId(重要蛙埂!這與極光推送等第三方SDK有關(guān))。
創(chuàng)建完畢后同步項(xiàng)目遮糖,會(huì)發(fā)現(xiàn)app moudle下的build.gradle文件內(nèi)容也發(fā)生了變化绣的,如下圖所示:
如果你可以熟練的使用gradle也可以選擇不使用AS提供的UI界面,直接編寫gradle文件欲账。
2 新建不同變體的sourceSet
在修改完變體的配置文件后屡江,我們還需要再項(xiàng)目的src文件夾下新建以我們的Flavor名稱命名的文件夾,并在這些文件夾下新建如main中相同的目錄結(jié)構(gòu)赛不。
我們正常編寫項(xiàng)目都是寫在main這個(gè)sourceSet下的惩嘉,但是如果我們的項(xiàng)目的變體有不同的資源文件、Java文件時(shí)踢故,我們就需要使用不同的sourceSet來區(qū)別開文黎。
需要注意的是,如果是資源文件殿较,F(xiàn)lavor下的資源文件會(huì)與main中的合并耸峭,如果存在重復(fù),則Flavor中優(yōu)先級(jí)高于main中淋纲。我們可以將不同變體中共用的資源存放在main中劳闹,只將不同的內(nèi)容存放在flavor的sourceSet中。
如果不同變體有內(nèi)容不同的Java文件則要注意帚戳,需要將這個(gè) Java 文件放置到每個(gè) flavor 的 sourceSet 文件夾下玷或,main中不可以有這個(gè)Java文件,如果main中也存在此文件片任,編譯時(shí)會(huì)提示文件重復(fù)偏友。比如說有兩個(gè)變體,有著不同的 MainActivity.java对供,那么 main 中就不能有這個(gè)文件了位他。需要把這個(gè) Java 文件放到各個(gè) flavor 的 sourceSet 下,同時(shí)這個(gè) Java 文件在 sourceSet 中要按照 main 中的包結(jié)構(gòu)保存产场。
3 AndroidManifest占位符
由于不同的項(xiàng)目有不同的名稱鹅髓、圖標(biāo),這一點(diǎn)我們可以通過類似上一步的方法京景,在不同的sourceSet中配置string.xml中的app_name屬性窿冯,與drawable文件夾中的ic_launcher。但是這樣有些麻煩确徙,當(dāng)我們的變體版本多了得手就需要不斷的重復(fù)這一動(dòng)作醒串,所以我使用的是在AndroidManifest文件中使用占位符然后在flavor中直接配置的方法执桌。這樣做的好處是,如果以后圖標(biāo)變更只需要到main中找到該文件然后替換即可芜赌,而不用去一個(gè)個(gè)找sourceSet仰挣。
首先將所有圖標(biāo)文件放到main中,然后在 AndroidManifest中使用¥{NAME}
格式的占位符缠沈,最后在flavor中使用manifestPlaceholders =[NAME1:VALUE1,NAME2:VALUE2]
替換占位符中的內(nèi)容膘壶。
在項(xiàng)目中,每個(gè)衍生版本都有自己的極光推送APPKEY屬性洲愤,這也可以是用占位符這一方法來處理颓芭,最終的flavors如下圖所示:
4 調(diào)試不同版本
現(xiàn)在我們擁有了三個(gè)不同的變體,但是我們調(diào)試的時(shí)候如何選擇對(duì)應(yīng)的程序來調(diào)試呢禽篱?
方式一:
方式二:
在選擇好要調(diào)試的變體后畜伐,會(huì)發(fā)現(xiàn)對(duì)應(yīng)的 sourceSet 文件夾變成了我們工程文件夾的央視:
可以看到我們一共有6個(gè)可選變體,這是怎么回事呢躺率?我們明明只設(shè)置了3個(gè)flavor玛界。
這時(shí)就需要介紹buildTypes了,再次回到項(xiàng)目配置頁面如下圖所示:
可以看到有兩個(gè)build type悼吱,這其中可以配置構(gòu)建的一些選項(xiàng)慎框,這里就不做過多介紹了。
我們的總變體數(shù)量等于 (build type數(shù)量)*(flavor數(shù)量)后添,這就是為什么一共有六個(gè)可選的variant笨枯。
最后獻(xiàn)上一個(gè)release的buildType配置
release {
minifyEnabled true
shrinkResources true //移除無用資源
debuggable false
zipAlignEnabled true //Zipalign優(yōu)化
proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'
signingConfig signingConfigs.uerbT
// 自定義輸出配置
applicationVariants.all { variant ->
variant.outputs.each { output ->
def outputFile = output.outputFile
if (outputFile != null && outputFile.name.endsWith('.apk')) {
// 輸出apk名稱為UerbT_v1.0_2016-12-01_uerbt.apk
def fileName = "UerbT_v${variant.versionName}_${releaseTime()}_${variant.flavorName}.apk"
output.outputFile = new File(outputFile.parent, fileName)
}
}
//過濾掉unaligned的包
variant.assemble.doLast {
variant.outputs.each { output ->
println "-----------------------------------------"
println "aligned " + output.outputFile
println "unaligned " + output.packageApplication.outputFile
File unaligned = output.packageApplication.outputFile;
File aligned = output.outputFile
if (!unaligned.getName().equalsIgnoreCase(aligned.getName())) {
println "deleting " + unaligned.getName()
unaligned.delete()
}
}
}
}
}
releaseTime() 函數(shù)如下:
def releaseTime() {
return new Date().format("yyyy-MM-dd", TimeZone.getTimeZone("UTC"))
}
參考閱讀:
重要-Gradle for Android 第四篇( 構(gòu)建變體 )
重要 - 使用gradle構(gòu)建不同特性的app
gradle配置詳解
知乎問答-如何使用gradle構(gòu)建不同的app
android studio gradle 多版本多apk打包
使用Gradle自動(dòng)化構(gòu)建多類型apk包
外包采用Gradle生成多套app打包
ANDROID STUDIO系列教程六--GRADLE多渠道打包
Android Studio中Gradle使用詳解