Android gradle(渠道)差异化打包 一:仅仅是针对一个module 二:针对多module的(根据不同依赖加载不同module并处理不同逻辑)

我们在开发项目的时候应该会遇到不同渠道有着不同的依赖或者处理不同逻辑,我们肯定不会因为由于不同渠道有这不同的代码,就维护两套不同的项目吧,所以这面想到了差异化打包。

起因:
我们的项目是有着国内国外发布渠道的,由于有些原因不同渠道有着不同依赖和不同的逻辑,一开始我们是通过不同依赖然后手动去改动代码,由于手动改动是有一定隐患的,所以想到了差异化打包 ,确保安全。

这面差异化打包一共有两种情况。

一:仅仅是针对一个module

    productFlavors {
        //国内
        domestic {

        }

        //国外
        external {

        }
    }


如图:
建立两个渠道,并在和main同目录下建立和main一样的包结构(如果不一样就会有问题),并且建立两个一样的处理类,里面处理不同的逻辑,但是main的包下是不能存在这个类的。

为啥main不能有呢,有的话编译会报错,存在同样的类异常。但是资源文件是可以有的。

class Utils {

    //展示Toast
    public static void showToast(Activity activity){
        Toast.makeText(activity,"domestic",Toast.LENGTH_LONG).show();
    }
}
class Utils {

    //展示Toast
    public static void showToast(Activity activity){
        Toast.makeText(activity,"external",Toast.LENGTH_LONG).show();
    }
}
  mBtnFlavors.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                Utils.showToast(MainActivity.this);
            }
        });

这样根据渠道打包,Utils就会根据渠道走对应的Utils类的逻辑了。

二:针对多module的(根据不同依赖加载不同module并处理不同逻辑)

还会有这种情况
因为FLAVOR是针对主module也就是application的,有一种情况是加载不同的代码对应不同module也就不在主module里面了,就像上面的这种情况, 显然第一种做法就不行了。
那面应该怎么做呢?
思路如下:
我这面用的是反射+接口+渠道打包

apply plugin: 'com.android.application'

android {
    compileSdkVersion 29
    buildToolsVersion "29.0.3"

    defaultConfig {
        applicationId "org.codefarml.differencebuild"
        flavorDimensions "default"
        minSdkVersion 16
        targetSdkVersion 29
        versionCode 1
        versionName "1.0"

        testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner"
    }

    buildTypes {
        release {
            minifyEnabled false
            proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro'
        }
    }

    productFlavors {
        //国内
        domestic {

        }

        //国外
        external {

        }
    }
}

//判断是否是国内
def isDomestic() {
    Gradle gradle = getGradle()
    String tskReqStr = gradle.getStartParameter().getTaskRequests().toString()
    println tskReqStr
    return tskReqStr.contains("Domestic")

}


dependencies {
    implementation fileTree(dir: "libs", include: ["*.jar"])
    implementation 'androidx.appcompat:appcompat:1.1.0'
    implementation 'androidx.constraintlayout:constraintlayout:1.1.3'
    testImplementation 'junit:junit:4.12'
    androidTestImplementation 'androidx.test.ext:junit:1.1.1'
    androidTestImplementation 'androidx.test.espresso:espresso-core:3.2.0'

    if(isDomestic()){
        api project(':domestic')
    }else {
        api project(':external')
    }
//
//    domesticImplementation project(':domestic')
//    externalImplementation project (':external')
}

根据不同的渠道加载不同的依赖库

    if(isDomestic()){
        api project(':domestic')
    }else {
        api project(':external')
    }
public interface IHandle {
    public void showToast(Activity activity);
}

公共接口类


两个module都应该实现这个接口类

package org.codefarml.domestic;

import android.app.Activity;
import android.widget.Toast;

import org.codefarml.common.IHandle;

/**
 * 文件    DifferenceBuild
 * 描述
 */
public class DomesticHandleImpl implements IHandle {
    @Override
    public void showToast(Activity activity) {
        Toast.makeText(activity,"domestic",Toast.LENGTH_LONG).show();
    }
}

package org.codefarml.external;

import android.app.Activity;
import android.widget.Toast;

import org.codefarml.common.IHandle;

/**
 * 文件    DifferenceBuild
 * 描述
 */
public class ExternalHandleImpl implements IHandle {
    @Override
    public void showToast(Activity activity) {
        Toast.makeText(activity,"external",Toast.LENGTH_LONG).show();
    }
}

这样差不多就搞定了,再看看调用方:

package org.codefarml.differencebuild;

import android.text.TextUtils;

import org.codefarml.common.IHandle;

/**
 * 文件    DifferenceBuild
 * 描述    通过反射+渠道处理差异类
 */
class ReflexBuildUtils {

    /**
     * 传入渠道的名字
     *
     * @param flavors
     */
    public static IHandle handleReflexBuild(String flavors) {

        Class currentClass;  //反射的class
        IHandle currentHandlePayImpl = null;  //反射的实例
        try {
            if (TextUtils.equals(flavors, "domestic")) {
                currentClass = Class.forName("org.codefarml.domestic.DomesticHandleImpl");
            } else {
                currentClass = Class.forName("org.codefarml.external.ExternalHandleImpl");
            }
            currentHandlePayImpl = (IHandle) currentClass.newInstance();

        } catch (Exception e) {
            e.printStackTrace();
            return null;
        }
        return currentHandlePayImpl;

    }
}
mBtnReflexAndFlavors.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                IHandle handleImpl = ReflexBuildUtils.handleReflexBuild(BuildConfig.FLAVOR);
                if(handleImpl != null){
                    handleImpl.showToast(MainActivity.this);
                }

            }
        });

ok,这样就大功告成了,依赖处会根据渠道依赖不同module,调用处会根据渠道反射不同的类来拿到实现类做当前渠道的逻辑。

github链接:
https://github.com/codeFarmL/DifferenceBuild

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