魏子翔,2017年加入去哪儿网技术团队,目前在平台事业部/大前端技术中心,主要负责Android平台的基础框架和跨端混合应用方向研发。关注前沿技术,热爱分享,拥抱开源。
简介
实际应用开发中,不可避免的会接触到多渠道打包,不过其实大家常用的多渠道打包其实分为两种。第一:只是需要简单的渠道标识,然后通过标识代码里做一些必要的逻辑处理,这种情况现在网上有很多开源的方案,可以做到快速打包,这里就不在多做介绍了。第二:需要对代码、资源、依赖、配置等做到更深度的定制,比如为不同的应用市场设置不同的启动页和 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
过滤变体
变体过滤器,以移除某些构建变体配置。
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设置。同样,Value/ 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。
参考阅读
推荐:官方文档 http://android.jobbole.com/84752/ https://juejin.im/post/58be7b8dac502e006c28869