我們在開發項目的時候應該會遇到不同渠道有着不同的依賴或者處理不同邏輯,我們肯定不會因爲由於不同渠道有這不同的代碼,就維護兩套不同的項目吧,所以這面想到了差異化打包。
起因:
我們的項目是有着國內國外發布渠道的,由於有些原因不同渠道有着不同依賴和不同的邏輯,一開始我們是通過不同依賴然後手動去改動代碼,由於手動改動是有一定隱患的,所以想到了差異化打包 ,確保安全。
這面差異化打包一共有兩種情況。
一:僅僅是針對一個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,調用處會根據渠道反射不同的類來拿到實現類做當前渠道的邏輯。