實際應用開發中,不可避免的會接觸到多渠道打包,不過其實你們經常使用的多渠道打包其實分爲兩種。第一:只是須要簡單的渠道標識,而後經過標識代碼裏作一些必要的邏輯處理,這種狀況如今網上有不少開源的方案,能夠作到快速打包,這裏就不在多作介紹了。第二:須要對代碼、資源、依賴、配置等作到更深度的定製,好比爲不一樣的應用市場設置不一樣的啓動頁和logo
,這種狀況就能夠採用官方的ProductFlavors
,下面也會詳細介紹這種方案。
簡單總結下這兩種方案,第一種打包速度快,可是不夠靈活,第二種有很強的定製性,可是因爲每次回從新編譯並簽名因此在打包速度上慢不少,你們能夠根據需求自由選擇不一樣的方案,或者搭配使用。java
首先須要在module
中的build.gradle
配置你須要的渠道,渠道中能夠修改一些defaultConfig
中的配置android
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個構建變體:app
構建變體:[common, xiaomi, huawei][debug, release]
對應 APK:app-[common, xiaomi, huawei]-[debug, release].apk
post
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)
}
}
...
}
複製代碼
現實場景中有的時候不一樣的渠道,提供的功能也不盡相同,這樣就須要對不一樣的渠道引入不一樣的組件包(前提App已經進行了組件拆分),以下簡單配置就能夠實現gradle
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
的構建類型,是不支持定製簽名的,具體緣由未知...ui
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'
}
}
複製代碼
有時咱們須要對Mainfest
中的某個屬性值作些調整,如配置不一樣渠道數據,App的Icon,還有替換聲明Activity等等,均可以經過下面的配置實現,若是感受這種簡單的調整還不足以知足你的需求,能夠看下方的定製源集的方案去深度的定製spa
// 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>
複製代碼
有時候簡單的調整可能不足以解決實際問題,這個時候能夠直接定製源集解決問題,找到youModule\src
,當前目錄下有個main
文件夾爲咱們工程的核心代碼和資源,咱們能夠在同級下建立不一樣的渠道目錄,如:common``xiaomi
等,此目錄能夠放置自定義的java代碼
、res資源
、AndroidManifest
、assets
等。
不一樣變體目錄(按優先級排列):debug
src/commonDebug/(構建變體源集)
src/debug/(buildTypes源集)
src/common/(productFlavors源集)
src/main/(主源集)
複製代碼
上面列出的順序決定了在 Gradle 合併代碼和資源時哪一個源集具備較高的優先級。若是 commonDebug/
和 debug/
包含相同的文件,Gradle 將使用 commonDebug/
源集中的文件。一樣,Gradle 會爲其餘源集中的文件賦予比 main/
中相同文件更高的優先級。Gradle 在應用如下構建規則時會考慮此優先級順序:調試
java/
下的源代碼只能有單一的類文件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>
複製代碼
對於習慣於使用命令構建的同窗來講有如下幾點須要補充