简介
实际应用开发中, 不可避免的会接触到多渠道打包, 不过其实大家常用的多渠道打包其实分为两种. 第一: 只是需要简单的渠道标识, 然后通过标识代码里做一些必要的逻辑处理, 这种情况现在网上有很多开源的方案, 可以做到快速打包, 这里就不在多做介绍了. 第二: 需要对代码, 资源, 依赖, 配置等做到更深度的定制, 比如为不同的应用市场设置不同的启动页和 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
参考阅读
推荐: 官方文档
- Android.jobbole.com/84752/ http://android.jobbole.com/84752/
- juejin.im/post/58be7b...
来源: https://juejin.im/post/5c09e996e51d45792642f7ba