实际应用开发中,不可避免的会接触到多渠道打包,不过其实你们经常使用的多渠道打包其实分为两种。第一:只是须要简单的渠道标识,而后经过标识代码里作一些必要的逻辑处理,这种状况如今网上有不少开源的方案,能够作到快速打包,这里就不在多作介绍了。第二:须要对代码、资源、依赖、配置等作到更深度的定制,好比为不一样的应用市场设置不一样的启动页和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>
复制代码
对于习惯于使用命令构建的同窗来讲有如下几点须要补充