Android Gradle基於參數化配置實現差異化構建

Android Gradle基於參數化配置實現差異化構建
一、背景:
項目中有一些特殊的需求,如個別渠道繼承騰訊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文件中配置了不少的如、及其他元數據等配置項。
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);
....
....
複製代碼
至此,完成基於參數化配置,實現特定渠道下的易觀集成的差異化構建。

四,結語
基於參數化配置實現差異化構建,需要依據實際的需求背景,分析具體的差異部分,以考慮簡便易行,同時兼顧易維護性爲主,實現具體的配置過程。

作者:HappyCorn
鏈接:https://juejin.im/post/5cc152f0e51d456e403772e9
來源:掘金

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章