1 背景
Android開發中你的模塊(Module)一般只有一個app主模塊,隨著功能不斷擴展你會發現一個模塊的缺點就是各種業務高度耦合,你就想測試登錄模塊,那么你可能會把支付模塊也編譯進去了,代價就是編譯耗時,降低效率。
大牛就提出了組件化開發的概念,假如一個App包括登錄,選購,支付三個模塊,那么就分成三個app,一個用來做登錄,一個用來展示商品讓用戶選購,一個用來做支付,這樣互相不影響而且編譯的話只會編譯當前模塊的代碼,提高了時間效率。
具體如何讓自己的App成為組件化App請看這位博主的博客
Android組件化開發
2 原理
既然知道了組件化開發,那么我們看下下面這張圖
箭頭表示依賴關系,舉例:BaseModule指向module1-login模塊、Module2-Pay模塊,表示在Login、Pay的build.gradle文件中包含對baseModule的依賴(即下面代碼)
compile project(path: ':baseModule')
app是我們的主模塊,module1模塊是登錄模塊,module2是支付模塊,baseModule是基礎模塊,baseModule的作用是搭建模塊間的橋梁,讓所有模塊共享資源,舉個例子:我把網絡請求框架,圖片緩存框架保存在baseModule里面,這樣module1和module2都依賴baseModule即可,每個module只關心做本業務的事就可以了,所有模塊都需要的事情交給baseModule去做即可。
主模塊可以和下面的所有子模塊通信調用,但是如果兩個子模塊之間需要調用該怎么弄呢?因此,我們需要搭建模塊間的橋梁,下面就是今天要說的Android模塊間通信了
假如登錄模塊的登錄頁面驗證完畢要跳轉到支付模塊的支付頁面應該如何做呢?
哼,這不簡單嗎?直接startAcitivity進行跳轉不就可以嗎?
哦!不對,module1沒有添加對module2的依賴,所以我無法獲取到module2中的PayActivity這個類!
哼,難得倒我?Class c=Class.forName(“類名”),反射直接拿到類,module1引用baseModule的方法進行跳轉!
不錯不錯,你會使用反射說明已經很有潛力解決這個問題了,讓我們來看下這個方法的問題出在哪里——你無法獲取這個類的名字,當我們合作開發的時候我們是不知道其他人的模塊里面的類定義是什么名字的(即使你知道名字,萬一有一天他改了包結構,類名變了呢?你的反射就會拋出異常了)
我知道這點小困難是難不住你的
那這樣吧,module1的開發者和module2的開發者說好,我們用一個標志來表示module2的支付頁面吧,比如叫
pay,我給你傳的參數是pay你就跳到你定義的支付頁面,test就跳轉到你的測試頁面,我不用管也不想管你以后怎
么定義你的類名和包名了
說干就干,我們在baseModule中定義一個接口用于表示要進行跳轉的標志,斜杠前面表示的模塊名,后面是表示的標志(為什么這樣定義后面會解釋)
下面是demo的效果,主頁中直接開啟module1,然后跳轉至module2,兩個子module相互點擊跳轉:
讓我們整理接下來的實現思路:
module1和module2均會實現baseModule的跳轉分發TaskDistribution接口,接口第二個參數會傳遞一個標志給
baseModule的接口TaskDistribution,Distributor為跳轉分發的實現類,里面利用反射創建各個子module的
跳轉類,據此就可以找到各個子module的頁面類,頁面類會根據這個標志flag進行判斷跳轉
解析:baseModule不依賴module2,他也是無法獲取支付類的,要想跳轉到支付類,只能把跳轉的任務交給module2,我們定義“module1/login”是為了保證baseModule分發標志的時候可以發給相應的模塊
接上面的分發跳轉任務這件事,那么module2中應該有一個類是要接受消息進行處理并且跳轉到相應的頁面的,因為每個模塊有有可能接受或者發送消息,所以都需要定義一個這樣的類,這樣我們就會想到在baseModule中定義一個收消息處理的接口,讓每個模塊實現這個接口去跳轉到本模塊的相應頁面
base中的消息轉發接口:
/**
* Description 每個模塊分發標志到對應的頁面
*/
public interface TaskDistribution {
void distribution(Context context, String flag, Object... objects);
}
子模塊1的跳轉實現:
public class Taskimp1 implements TaskDistribution{
@Override
public void distribution(Context context, String flag, Object... objects) {
if(flag.endsWith("login")){
//跳轉到登錄頁面
//context.startActivity(...);
}
if(flag.endsWith("login")){
Intent intent = new Intent(context, ModuleOneActivity.class);
context.startActivity(intent);
}
}
}
子模塊2的跳轉實現:
public class Taskimp2 implements TaskDistribution {
@Override
public void distribution(Context context, String flag, Object... objects) {
if(flag.endsWith("pay")){
Intent intent = new Intent(context, ModuleTwoActivity.class);
context.startActivity(intent);
}
}
}
到這里僅解決了任務分發到各個子module里面后的啟動activity的邏輯,但是在baseModule中怎么拿到各個子工程的引用呢?
新的問題來了…..你是baseModule,你怎么可能拿到Taskimp2和Taskimp1兩個類呢?你不依賴他們,而是他們依賴于你呢!
天無絕人之路!
那些有潛力的小伙子終于到你們大顯身手的時候了,反射派上用場了!既然我拿不到每個類的名字,那我拿到轉發器Taskimp1、Taskimp2的類名總可以了吧?(注意為什么拿轉發器而不是類的名字,以為類是很多的,我們不可能知道所有的類的名字,但是我們只定義一個轉發器,這個是固定的,所以獲取轉發器的名字更為現實)
baseModule利用反射獲取到非依賴的類全限定名,進行跳轉轉發:
/**
* Description 用于獲取模塊轉發器的類名
*/
public class Distributor {
private static HashMaphashMap = new HashMap<>();
private static TaskDistribution taskDistribution;
public static void init() {
hashMap.put("m1", "ctrip.module1.Taskimp1");
hashMap.put("m2", "ctrip.module2.Taskimp2");
}
private static void getTaskDistribution(String flag) {
try {
Class c = null;
if (flag != null && flag.startsWith("module1")) {
c = Class.forName(hashMap.get("m1"));
}
if (flag != null && flag.startsWith("module2")) {
c = Class.forName(hashMap.get("m2"));
}
taskDistribution = (TaskDistribution) c.newInstance();
} catch (ClassNotFoundException e) {
e.printStackTrace();
} catch (IllegalAccessException e) {
e.printStackTrace();
} catch (InstantiationException e) {
e.printStackTrace();
}
}
public static void turn2Acitivity(Context context, String flag, Object... objects) {
getTaskDistribution(flag);
taskDistribution.distribution(context, flag, objects);
}
}
這個Distributor我們把所有的方法耦合到一個類里面其實是不太好的,比如hashMap用不用呢?比如能不能自由選擇添加module而不是init把所有的module都添加進去呢?完全自己可以根據業務需求擴展,但是源頭就是在這個類中。
總結下來我們就會發現這個模塊間通信其實用的就是:反射+多態
項目地址:https://github.com/buder-cp/DesignPattern/tree/master/Android-Module-Protocol-master
參考:https://blog.csdn.net/LosingCarryJie/article/details/78760204