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);
....
....
複製代碼
至此,完成基於參數化配置,實現特定渠道下的易觀集成的差別化構建。
四,結語 基於參數化配置實現差別化構建,須要依據實際的需求背景,分析具體的差別部分,以考慮簡便易行,同時兼顧易維護性爲主,實現具體的配置過程。