簡介
實際應用開發中,不可避免的會接觸到多渠道打包,不過其實大家常用的多渠道打包其實分為兩種。第一:只是需要簡單的渠道標識,然後通過標識程式碼裡做一些必要的邏輯處理,這種情況現在網上有很多開源的方案,可以做到快速打包,這裡就不在多做介紹了。第二:需要對程式碼、資源、依賴、配置等做到更深度的定製,比如為不同的應用市場設定不同的啟動頁和logo
,這種情況就可以採用官方的ProductFlavors
,下面也會詳細介紹這種方案。
簡單總結下這兩種方案,第一種打包速度快,但是不夠靈活,第二種有很強的定製性,但是由於每次回重新編譯並簽名所以在打包速度上慢很多,大家可以根據需求自由選擇不同的方案,或者搭配使用。
方案介紹
構建配置
首先需要在module
中的build.gradle
配置你需要的渠道,渠道中可以修改一些defaultConfig
中的配置
android {
···
defaultConfig {
minSdkVersion 19
versionCode 1
...
}
// 渠道的維度,支援不同維度的渠道
flavorDimensions "channel"
productFlavors {
common {
dimension "channel"
}
xiaomi {
minSdkVersion '21'
versionCode 20000 + android.defaultConfig.versionCode
versionNameSuffix "-minApi21"
dimension "channel"
}
huawei {
minSdkVersion '23'
versionCode 20000 + android.defaultConfig.versionCode
versionNameSuffix "-minApi23"
dimension "channel"
}
}
buildTypes {
debug {
minifyEnabled false
proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'
}
release {
minifyEnabled false
proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'
}
}
...
}
複製程式碼
Gradle 會通過上面的配置建立維度 * 維度中的渠道 * 構建型別數量的構建變體。在 Gradle 為對應構建變體的APK 命名時,首先是渠道,之後是構建型別。以上面的構建配置為例,Gradle 可以使用以下命名方案建立共6個構建變體:
構建變體:[common, xiaomi, huawei][debug, release]
對應 APK:app-[common, xiaomi, huawei]-[debug, release].apk
過濾變體
Gradle
會為每個可能的組合建立構建變體。都在Android Studio -> Build Variants
中顯示出來,不過某些特定的構建變體在您的專案環境中並不必要,也可能沒有意義。您可以在build.gradle
檔案中建立一個變體過濾器,以移除某些構建變體配置。
android {
···
variantFilter { variant ->
def names = variant.flavors*.name
def buildTypeName = variant.buildType.name
println (names + "==" + buildTypeName)
// 這樣就會移除 commonDebug的變體
if (buildTypeName.contains("debug") && names.contains("common")) {
setIgnore(true)
}
}
...
}
複製程式碼
dependencies依賴
現實場景中有的時候不同的渠道,提供的功能也不盡相同,這樣就需要對不同的渠道引入不同的元件包(前提App已經進行了元件拆分),如下簡單配置就可以實現
configurations {
// Gradle沒有提供此細粒度級別的依賴方式,需要自己配置下不然會報錯
xiaomiDebugImplementation {}
}
dependencies {
implementation fileTree(dir: 'libs', include: ['*.jar'])
implementation('com.android.support:appcompat-v7:26.1.0')
// 可以控制 xiaomi渠道下 的 debug 構建型別才去引入此包
xiaomiDebugImplementation('com.xxx:xxx:1.6.0')
debugImplementation('com.xxx:xxx:1.6.0')
commonImplementation('com.xxx:xxx:1.6.0')
}
複製程式碼
不同渠道的獨立簽名
同上面需求,對於功能不同的安裝包,大概率是要獨立的簽名,通過簡單的配置一樣可以實現,不過對於debug
的構建型別,是不支援定製簽名的,具體原因未知...
signingConfigs {
test11 {
storeFile file("../test11.keystore")
storePassword 'test11'
keyAlias 'test11'
keyPassword 'test11'
}
test22 {
storeFile file("../test22.keystore")
storePassword 'test22'
keyAlias 'test22'
keyPassword 'test22'
}
}
// 渠道的維度,支援不同維度的渠道
flavorDimensions "channel"
productFlavors {
common {
dimension "channel"
}
xiaomi {
dimension "channel"
}
huawei {
dimension "channel"
}
}
buildTypes {
debug {
//debug定製簽名無效 只能指定一個或者使用預設的簽名
// productFlavors.huawei.signingConfig signingConfigs.test11
// productFlavors.xiaomi.signingConfig signingConfigs.test22
// productFlavors.common.signingConfig signingConfigs.test11
minifyEnabled false
proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'
}
release {
productFlavors.huawei.signingConfig signingConfigs.test11
productFlavors.xiaomi.signingConfig signingConfigs.test22
productFlavors.common.signingConfig signingConfigs.test11
minifyEnabled false
proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'
}
}
複製程式碼
Manifest配置
有時我們需要對Mainfest
中的某個屬性值做些調整,如配置不同渠道資料,App的Icon,還有替換宣告Activity等等,都可以通過下面的配置實現,如果感覺這種簡單的調整還不足以滿足你的需求,可以看下方的定製源集的方案去深度的定製
// build.gradle
android {
···
flavorDimensions "channel"
productFlavors {
common {
dimension "channel"
manifestPlaceholders = ["ChannelData" : "Common Meta Data",
"AppIcon" : "@mipmap/ic_common",
"MainActivity":CommonActivity"]
}
xiaomi {
dimension "channel"
manifestPlaceholders = ["ChannelData" : "XiaoMi Meta Data",
"AppIcon" : "@mipmap/ic_launcher",
"MainActivity":"XMActivity"]
}
huawei {
dimension "channel"
manifestPlaceholders = ["ChannelData" : "HuaWei Meta Data",
"AppIcon" : "@mipmap/ic_launcher",
"MainActivity": "HWActivity"]
}
}
...
}
// Manifest
<application
//${AppIcon} 替換AppIcon
android:icon="${AppIcon}"
... >
//${ChannelData} 替換ChannelData
<meta-data
android:name="ChannelData"
android:value="${ChannelData}"/>
//${ChannelData} 替換宣告Activity
<activity android:name="${MainActivity}">
...
</activity>
</application>
複製程式碼
定製程式碼 資源 Manifest 等源集
有時候簡單的調整可能不足以解決實際問題,這個時候可以直接定製源集解決問題,找到youModule\src
,當前目錄下有個main
資料夾為我們工程的核心程式碼和資源,我們可以在同級下建立不同的渠道目錄,如:common``xiaomi
等,此目錄可以放置自定義的java程式碼
、res資源
、AndroidManifest
、assets
等。
不同變體目錄(按優先順序排列):
src/commonDebug/(構建變體源集)
src/debug/(buildTypes源集)
src/common/(productFlavors源集)
src/main/(主源集)
複製程式碼
上面列出的順序決定了在 Gradle 合併程式碼和資源時哪個源集具有較高的優先順序。如果 commonDebug/
和 debug/
包含相同的檔案,Gradle 將使用 commonDebug/
源集中的檔案。同樣,Gradle 會為其他源集中的檔案賦予比 main/
中相同檔案更高的優先順序。Gradle 在應用以下構建規則時會考慮此優先順序順序:
- 對於
java/
下的原始碼只能有單一的類檔案
注:對於給定的渠道目錄,如果找到兩個或兩個以上定義同一 Java 類的源集目錄,Gradle 就會引發一個構建錯誤。例如,在構建除錯 APK 時,您不能同時定義src/common/Utility.java
和src/main/Utility.java
。這是因為 Gradle 會在構建過程中檢查這兩個目錄並引發duplicate class
錯誤。如果針對不同的構建型別需要不同版本的Utility.java
,您可以讓每個渠道定義其自己的檔案版本,如:src/common/Utility.java
和src/xiaomi/Utility.java
,而不將其包含在 main/ 中。 - 所有
Manifest
合併為單個Manifest
。將按照上述列表中的相同順序指定優先順序。也就是說,某個構建型別的Manifest
設定會替換某個渠道的Manifest
設定 - 同樣,
values/ res/ 和 asset/
目錄中的如果存在有兩個或兩個以上的同名資源,比如在渠道中的資源將會替換main中資源,以下對於同時存在於strings.xml
的同名資源和資源圖示做個示例
// main 下的 圖示資源
main\res\mipmap-hdpi\ic_launcher.png
// 在 xiaomi 下的 圖示資源
xiaomi\res\mipmap-hdpi\ic_launcher.png
//打包 xiaomi 渠道的時候會自動替換圖片。
複製程式碼
// main 下的 strings.xml
<resource>
<string name="app_name">MultiChannel</string>
<string name="string_merge">我是string,沒被合併</string>
</resource>
// 在 xiaomi 下的 strings.xml 內容為:
<resource>
<string name="string_merge">我是xiaomi,已經合併</string>
</resource>
//當打 xiaomi 渠道包時,最終 strings.xml 會變成:
<resource>
<string name="app_name">MultiChannel</string>
<string name="string_merge">我是xiaomi,已經合併</string>
</resource>
複製程式碼
其他
命令構建
對於習慣於使用命令構建的同學來說有以下幾點需要補充
- 打全部包: gradle assemble
- 打全部 Debug 包: gradle assembleDebug ,可以簡寫為 gradle aD 或 aDebug
- 打全部 Release 包: gradle assembleRelease,可以簡寫為 gradle aR 或 aRelease
- 打指定 flavor 包: gradle assemble(flavor)(Debug|Release) 如:gradle assembleXiaomiDebug
- 打包完成後安裝: gradle install(flavor)(Debug|Release)如:如:gradle installXiaomiDebug
- 打包前先 clean 一下,在測試的時候很必要: gradle clean assembleXiaomiDebug