The background,

There are some special requirements in the project, such as the integration of Tencent Bugly for individual channels, the integration of Yanyanstatistics for individual channels, the integration of different push strategies for different channels (for example, Oppo channel gives priority to Opush push), different channels have different third-party login integration, etc. These requirements themselves are often related to external integrated functions, and there is a certain mapping relationship between functions and channels. For such requirements, specific project construction can have the following strategies: 1. Different branch management to correspond to different differentiated implementation; 2. Realize different differentiation construction through variation; 3. Differentiated construction can be realized through the parametric configuration of Android Gradle.


2. Analysis of the advantages and disadvantages of the scheme

1. Based on different branch management, the different part of the code is directly in the special branch, each time needs to merge with the main branch and resolve the possible merge conflict. At the same time, for the special channel logic, if the code is isolated through branches, often the individual developers are based on the main branch development, the difference of the channel logic processing part is easy to ignore, sometimes causing unnecessary bugs and other situations, high maintenance costs.

2. Differentiated construction based on variants, Gradle variants are directly used, with the advantage that the variants directory and corresponding construction process are automatically included. Corresponding, too elegant is such a demand once the multifarious, variant types and corresponding directory level relative increase in the number of variation types will be increased with the increase of product flavor doubling number increase, in the concrete construction, build tasks will be relatively complicated, and the corresponding in the build directory, such as the output of the directory hierarchy is relatively complex.

3, based on the parametric configuration Gradle, details according to the specific requirements, configuration and actively deal with the corresponding differentiation build logic, such as channel mapping relationship, different external dependencies, as well as the corresponding code placeholder, etc., in keep original variants under the condition of invariable constant and build tasks, simply by parametric configuration, The corresponding differentiation part construction can be completed.

This paper mainly discusses the implementation scheme of “differentiated construction through parametric configuration”. The specific implementation process is discussed in detail below through individual channels integrating Bugly and analytica statistics.


Three, the instance,

1. The integration of Bugly in individual channels is relatively simple, including introducing bugly dependency in build.gradle, initializing Bugly in appropriate locations (such as Application), and confusing bugly configuration in Proguard.cfg. But in this case, the Bugly integration is not targeted to the main project itself, but to a specific channel. Specific parametric configuration to achieve differentiated construction process is as follows: a. Create ext.gradle file in the main project to realize logical mapping of channels:

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'}}Copy the code

The specific logical mapping includes: 1.1, channel receiving and logical judgment addBugly; 1.2, the introduction of bugly dependency under corresponding logical confirmation (addBugly); 1.3, corresponding to the source set specified under logical validation.

B, introduce Ext.gradle into the main project

apply from: '.. /ext.gradle'
Copy the code

C, process the corresponding source set logic in the corresponding module of the project (base module as an example)

Base/SRC/main/Java/com/mycorn - the default project source code base/SRC/ext/bugly/com/mycorn - bugly logic to confirm the additional source set source directory Base/SRC/mock/bugly/com/mycorn - usually set of additional source base source directory/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..."; / / initialize the tencent bugly is the third parameter indicates whether or not the debug mode 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"); // Is actually an empty method, mainly used as a placeholder}}Copy the code

D. Under the main project of the project, the universal bugly initialization placeholder logic should be directly written in the corresponding place of initialization Bugly

. . com.mycorn.BuglyHelper.initBugly(context); . .Copy the code

E, the proguard. CFG configuration item can be directly added to the proguard. CFG file of the corresponding module because the configuration is only for code confusion

. .# tencent bugly-dontwarn com.tencent.bugly.** -keep public class com.tencent.bugly.**{*; }... .Copy the code

So far, the differentiation construction introduced by Tencent Bugly based on parametric configuration has been completed.

The key point lies in the processing of the corresponding “placeholder” logic.

2. The integration of analysys statistics in individual channels is generally similar to the above integration of Tencent Bugly. The special point is that the access project of Analysys statistics is directly introduced jar files. In the corresponding androidmanifest.xml file, a lot of configuration items such as

,

and other metadata are configured. When building Android Gradle projects, you can use sourceSets to add source code and resource directories for the same module, but you cannot add AndroidManifest files. Manifest.srcfile is currently only a reset of the AndroidManifest file. However, if it is a standalone module, or if there are already independent external AAR dependencies introduced, Android Gradle will automatically implement the corresponding AndroidManifest file merge when it is built. Therefore, in order to be able to separate the AndroidManifest configuration item of looswatch statistics, it is necessary to separate looswatch statistics into independent modules or corresponding AAR files based on the above example (this example is to illustrate the specific solution, for the latest looswatch statistics if the dependency introduction has been supported, it is not discussed).

A, will be easy to form an independent module, AndroidManifest, libs directory jar package, proguard. CFG file, etc., to achieve independent configuration;

B. Refer to the integration of Bugly in the above example to deal with the corresponding observable logic relations

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')}}Copy the code

C, form easy source set logic under the same corresponding directory, and change to common logical placeholder notation where initialization is required.

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"); // Is actually an empty method, mainly used as a placeholder}}Copy the code
. . EguanHelper.initEguan(this); . .Copy the code

So far, the differential construction of easy integration under specific channels is realized based on parametric configuration.


Four, conclusion

To realize the differentiation construction based on parametric configuration, it is necessary to analyze the specific differences according to the actual demand background, and realize the specific configuration process based on simple and easy operation and easy maintenance.