一, 背景:
项目中有一些特殊的需求, 如个别渠道继承腾讯 bugly, 个别渠道集成易观统计, 不同的渠道集成不同的推送策略(如 Oppo 渠道优先 Opush 推送), 不同的渠道拥有不同的第三方登录集成等等. 这些需求本身, 往往都与外部集成进来的功能有关, 且需求上, 功能与渠道本身, 有一定的映射关系, 对于此类需求, 具体项目构建时可以有如下几种策略:
1, 不同的分支管理, 以对应不同的差异化实现;
2, 通过变体, 实现不同的差异化构建;
3, 通过 Android Gradle 参数化配置, 实现差异化构建.
二, 方案利弊分析:
1, 基于不同的分支管理, 差异部分的代码直接在特殊分支中, 每次需要与主分支进行合并并解决可能的合并冲突. 同时, 针对特殊的渠道逻辑, 如果代码通过分支隔离, 往往开发个体都是基于主分支开发, 渠道的差异性逻辑处理部分容易忽略, 有时候造成不必要的 bug 等情形, 维护成本较大.
2, 基于变体的差异化构建, 直接使用 Gradle 变体方案, 优势在于变体目录及对应的构建流程已经自动包含. 对应的, 不太优雅的地方在于此类需求一旦繁杂, 变体的种类及对应的目录层次相对增多, 变体类型会随着产品风味的增加而成倍数增长, 在具体构建时, 构建任务也会相对繁杂, 且对应在 build 等目录下的输出的目录层次也相对复杂.
3, 基于 Gradle 的参数化配置, 依据具体的需求详情, 主动配置并处理对应的差异化构建逻辑, 如渠道的映射关系, 不同的外部依赖, 以及对应的代码占位等, 以此在保持原有变体不变和构建任务不变的情况下, 只需通过参数化的配置, 即可完成对应的差异化部分构建.
本文主要讨论 "通过参数化配置实现差异化构建" 实现方案. 下面通过个别渠道集成 bugly 和易观统计详细讨论具体的实现过程.
三, 实例
1, 个别渠道的 bugly 集成 主工程如果要集成 bugly, 相对非常简单, 主要包括 build.gradle 中引入 bugly 依赖, 适当位置 (如 Application 中) 初始化 bugly,proguard.cfg 中进行 bugly 的混淆配置. 但本例中, bugly 集成不是针主工程本身, 而是针对特定的渠道. 具体的参数化配置实现差异化构建过程如下:
a, 项目主工程中新建 ext.gradle 文件, 实现对渠道的逻辑映射:
- ext.gradle
- --------------------------
- ext {
- channel = project.hasProperty('channel') ? channel : 'feature'
- addBugly = {
- def buglyChannelList = ["huawei"]
- def result = buglyChannelList.contains(channel)
- println ">>> channel:${channel}, bugly added:${result}"
- if(result) {
- return true
- }
- return false
- }
- }
- Android {
- sourceSets {
- main{
- java {
- if(addBugly()) {
- srcDirs "src/ext/bugly/java"
- } else {
- srcDirs "src/mock/bugly/java"
- }
- }
- }
- }
- }
- dependencies {
- if (addBugly()) {
- API 'com.tencent.bugly:crashreport:latest.release'
- API 'com.tencent.bugly:nativecrashreport:latest.release'
- }
- }
具体的逻辑映射包括:
1.1, 渠道值 (channel) 的接收和逻辑判断 addBugly;
1.2, 对应逻辑确认下 (addBugly) 的 bugly 依赖引入;
1.3, 对应逻辑确认下的源集指定.
b, 项目主工程中引入 ext.gradle
apply from: '../ext.gradle'
c, 项目对应模块中, 处理对应的源集逻辑(base 模块为例)
base/src/main/java/com/mycorn --- 默认工程源码
base/src/ext/bugly/com/mycorn ---bugly 逻辑确认下的额外源集源码目录
base/src/mock/bugly/com/mycorn --- 通常情况下的额外源集源码目录
- base/src/ext/bugly/com/mycorn
- ---------------------------------
- package com.mycorn;
- import Android.App.Application;
- import Android.util.Log;
- public class BuglyHelper {
- public static final String TAG = "BuglyHelper";
- public static void initBugly(Application context) {
- Log.d(TAG, "bugly init...";
- // 初始化腾讯 bugly 第三个参数表示是否处于调试模式
- com.tencent.bugly.crashreport.CrashReport.initCrashReport(context, "bbccdd123456", false);
- }
- }
- base/src/mock/bugly/com/mycorn
- ---------------------------------
- package com.mymoney;
- import Android.App.Application;
- import Android.util.Log;
- public class BuglyHelper {
- public static final String TAG = "BuglyHelper";
- public static void initBugly(Application context) {
- Log.d(TAG, "bugly init...mock");
- // 实际上是空方法, 主要是用于占位
- }
- }
d, 项目主工程下, 在对应初始化 bugly 的地方直接写上通用性的 bugly 初始化占位逻辑
- ....
- ....
- com.mymoney.BuglyHelper.initBugly(context);
- ....
- ....
e,proguard.cfg 配置项, 由于只是进行代码混淆的配置, 此处可以直接放到对应模块的 proguard.cfg 文件中
- ....
- ....
- # 腾讯 bugly
- -dontwarn com.tencent.bugly.**
- -keep public class com.tencent.bugly.**{*;}
- ....
- ....
至此, 基于参数化配置实现腾讯 bugly 引入的差异化构建, 得以完成.
其中关键点, 在于对应的 "占位" 逻辑的处理.
2, 个别渠道的易观统计集成 总体上与上述的腾讯 bugly 集成类似, 特别之处在于易观统计的接入项目中是直接引入的 jar 文件, 并在对应的 AndroidManifest.xml 文件中配置了不少的如 < service>,<receiver > 及其他元数据等配置项.
Android Gradle 项目构建时, 对于同一模块, 可以通过 sourceSets 增加如源码及资源目录等, 但却不能增加 AndroidManifest 文件, 形如 manifest.srcFile 的写法当前只能是对 AndroidManifest 文件的重新设定. 但如果是独立模块, 或已经是独立的外部 aar 等依赖引入, Android Gradle 构建时会自动实现对应的 AndroidManifest 文件合并. 因此, 为了能够将易观统计中的 AndroidManifest 配置项进行单独隔离, 需要在上例中的基础上将易观统计单独隔离成独立模块, 或对应的 aar 文件等(本例在于阐述具体解法, 对于最新的易观统计如果已经支持依赖引入, 则不在讨论范围内).
a, 将易观形成独立模块, AndroidManifest,libs 目录下的 jar 包, proguard.cfg 文件等, 实现独自配置;
b, 参照上例 bugly 的集成, 处理对应的易观逻辑关系
- ext {
- channel = project.hasProperty('channel') ? channel : 'feature'
- addBugly = {
- def buglyChannelList = ["huawei"]
- def result = buglyChannelList.contains(channel)
- println ">>> channel:${channel}, bugly added:${result}"
- if(result) {
- return true
- }
- return false
- }
- addEguan = {
- def eguanChannelList = ["baidu"]
- def result = eguanChannelList.contains(channel)
- println ">>> channel:${channel}, eguan added:${result}"
- if(result) {
- return true
- }
- return false
- }
- }
- Android {
- sourceSets {
- main{
- java {
- if (addBugly()) {
- srcDirs "src/ext/bugly/java"
- } else {
- srcDirs "src/mock/bugly/java"
- }
- if (addEguan()) {
- srcDirs "src/ext/eguan/java"
- } else {
- srcDirs "src/mock/eguan/java"
- }
- }
- }
- }
- }
- dependencies {
- if (addBugly()) {
- API 'com.tencent.bugly:crashreport:latest.release'
- API 'com.tencent.bugly:nativecrashreport:latest.release'
- }
- if (addEguan()) {
- API project(':eguan')
- }
- }
c, 同样的对应的目录下形成易观的源集逻辑, 并在需要初始化的地方, 改成通用的逻辑占位写法.
- base/src/ext/eguan/com/mycorn
- ---------------------------------
- package com.mycorn;
- import Android.content.Context;
- import Android.util.Log;
- import com.eguan.monitor.EguanMonitorAgent;
- public class EguanHelper {
- public static final String TAG = "EguanHelper";
- public static void initEguan(Context context) {
- Log.d(TAG, "eguan init...");
- try {
- EguanMonitorAgent.getInstance().initEguan(context, "4909454903452702a", "feidee");
- } catch (Exception e) {
- Log.d(TAG, "eguan init exception...");
- }
- }
- }
- base/src/mock/eguan/com/mycorn
- ---------------------------------
- package com.mycorn;
- import Android.content.Context;
- import Android.util.Log;
- public class EguanHelper {
- public static final String TAG = "EguanHelper";
- public static void initEguan(Context context) {
- Log.d(TAG, "eguan init...mock");
- // 实际上是空方法, 主要是用于占位
- }
- }
- ....
- ....
- EguanHelper.initEguan(this);
- ....
- ....
至此, 完成基于参数化配置, 实现特定渠道下的易观集成的差异化构建.
四, 结语
基于参数化配置实现差异化构建, 需要依据实际的需求背景, 分析具体的差异部分, 以考虑简便易行, 同时兼顾易维护性为主, 实现具体的配置过程.
来源: https://www.cnblogs.com/lwbqqyumidi/p/10771289.html