1、背景:
项目中有一些特殊的需求,如个别渠道集成腾讯bugly,个别渠道集成易观统计,不一样的渠道集成不一样的推送策略(如Oppo渠道优先Opush推送),不一样的渠道拥有不一样的第三方登陆集成等等。这些需求自己,每每都与外部集成进来的功能有关,且需求上,功能与渠道自己,有必定的映射关系,对于此类需求,具体项目构建时能够有以下几种策略:
1,不一样的分支管理,以对应不一样的差别化实现;
2,经过变体,实现不一样的差别化构建;
3,经过Android Gradle参数化配置,实现差别化构建。java
2、方案利弊分析:
1,基于不一样的分支管理,差别部分的代码直接在特殊分支中,每次须要与主分支进行合并并解决可能的合并冲突。同时,针对特殊的渠道逻辑,若是代码经过分支隔离,每每开发个体都是基于主分支开发,渠道的差别性逻辑处理部分容易忽略,有时候形成没必要要的bug等情形,维护成本较大。android
2,基于变体的差别化构建,直接使用Gradle变体方案,优点在于变体目录及对应的构建流程已经自动包含。对应的,不太优雅的地方在于此类需求一旦繁杂,变体的种类及对应的目录层次相对增多,变体类型会随着产品风味的增长而成倍数增加,在具体构建时,构建任务也会相对繁杂,且对应在build等目录下的输出的目录层次也相对复杂。api
3,基于Gradle的参数化配置,依据具体的需求详情,主动配置并处理对应的差别化构建逻辑,如渠道的映射关系,不一样的外部依赖,以及对应的代码占位等,以此在保持原有变体不变和构建任务不变的状况下,只需经过参数化的配置,便可完成对应的差别化部分构建。bash
本文主要讨论“经过参数化配置实现差别化构建”实现方案。 下面经过个别渠道集成bugly和易观统计详细讨论具体的实现过程。app
三,实例
1,个别渠道的bugly集成 主工程若是要集成bugly,相对很是简单,主要包括build.gradle
中引入bugly依赖,适当位置(如Application
中)初始化bugly,proguard.cfg
中进行bugly的混淆配置。但本例中,bugly集成不是针主工程自己,而是针对特定的渠道。具体的参数化配置实现差别化构建过程以下:
a,项目主工程中新建ext.gradle
文件,实现对渠道的逻辑映射:post
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,对应逻辑确认下的源集指定。gradle
b,项目主工程中引入ext.gradle
ui
apply from: '../ext.gradle' 复制代码
c,项目对应模块中,处理对应的源集逻辑(base模块为例)this
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.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...mock"); // 其实是空方法,主要是用于占位 } } 复制代码
d,项目主工程下,在对应初始化bugly的地方直接写上通用性的bugly初始化占位逻辑spa
....
....
com.mycorn.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, "111222333", "baidu"); } 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);
....
....
复制代码
至此,完成基于参数化配置,实现特定渠道下的易观集成的差别化构建。
四,结语 基于参数化配置实现差别化构建,须要依据实际的需求背景,分析具体的差别部分,以考虑简便易行,同时兼顾易维护性为主,实现具体的配置过程。