預備知識
- Android 基礎知識
- LiveData的基本定義和用法
- 開源消息框架EventBus的基本瞭解
看完本文能夠學到什麼
- Android項目組件化/模塊化
- 組件通信框架Router/EventBus的探究
- 用LiveData來實現EventBus
一、Android項目組件化/模塊化
開發Android項目之初,項目業務比較單一,採用單Project的形式存在,隨着業務線越來越多,團隊的日益壯大,單項目的弊端是越來越明顯。整個團隊在單項目下開發,使得代碼越來越臃腫,代碼提交衝突多,業務線耦合嚴重。於是聰明的開發們提出了項目組件化/模塊化。
組件化圖
現在基本上是這種模式了。我們現在主要關注的是moduleA、moduleB、moduleC、moduleD等module之間的通信。業務模塊之間的關係是相互獨立的,這樣最大好處是業務代碼解耦,開發者只需要管理自己的模塊就OK了。但是很多場景是需要業務模塊之間通信的,比如moduleA需要調用moduleB的一個方法,我們一開始module定義的接口放在業務支撐庫common/base中,這樣moduleA通過Router反射就能調用了。後面發現這樣還是不方便,因爲大家發現只要業務模塊有重複邏輯或者暴露接口都往base裏面放,這樣慢慢的base也就變得臃腫了。
在此基礎上我們改造了一下,不動base業務支撐庫。將module分爲業務模塊和接口模塊(extension)。
組件通信
我們將接口定義在extension模塊中,提供給自己和其他的module調用。這樣我們可以不用再去改base了。又可以愉快的玩耍了。
當然根據某些場景來說,比如moduleA調用方法之後需要moduleB、moduleC、moduleD同時去更新UI,Router的能力就沒法辦到了,也就需要另外一種組件通信方式EventBus。EventBus這個框架很優秀,很多項目也會採用EventBus來彌補Router的不足。
但是個人認爲EventBus帶來的代碼可讀性太差,且約束力太差,開發者可以隨意發即時消息,導致消息的錯用濫用。這對團隊開發來說效率反而是降低的。所以一般公司不會採用EventBus,都會自己研發消息框架。所以我們第一版消息框架出來了,關鍵代碼如下:
private Map<String, List<WeakReference<MessageReceiver>>> messageReceivers = new ConcurrentHashMap();
//消息接受方
public interface MessageReceiver {
void onReceive(@NonNull Message message);
}
我們爲消息定了消息名,作爲Map的key,而接收方是實現了MessageRecevier的對象,發送消息的時候根據消息名取到註冊了MessageReceiver的List遍歷,執行MessageReceiver.onReceive(),這個跟系統帶的Observable與Observer是一樣的,在單項目下也堅挺了一段時間。後面項目完全組件化了,暴露出來的問題越來越大。
- 消息容易重名,因爲各個module都是有自己的研發團隊管理的,開發者對自己的模塊是比較熟悉的,對於其他不負責的模塊是兩眼一抹黑,完全不瞭解了。且如果幾個業務模塊都需要收到消息,需要定一個統一的消息名,最好能夠下沉到Base庫,大家都能引用到,下沉這個事情我想大家內心是拒絕的。
- 消息沒有綁定Activity/fragment的生命週期,可能造成內存泄漏。
- 消息約束力太差,容易造成消息錯用濫用,特麼好的沒有學到,EventBus的毛病全學到了。
爲了解決以上痛點,借鑑了美團的消息框架,決定用LiveData來打造屬於你的EventBus:
- 消息名由moduleName + eventName組成,保證了唯一性,解決不同組件定義了重名消息的問題採用兩級HashMap的方式解決。第一級HashMap的構建以ModuleName作爲Key,第二級HashMap作爲Value;第二級HashMap以消息名稱EventName作爲Key,LiveData作爲Value。查找的時候先用組件名稱ModuleName在第一級HashMap中查找,如果找到則用消息名EventName在第二級HashName中查找。
- 使用LiveData構建消息總線具有生命週期感知能力,使用者不需要調用反註冊,相比EventBus和RxBus使用更爲方便,並且沒有內存泄漏風險。
- 消息總線框架增強以下約束:
- 只能訂閱和發送在組件中預定義的消息。換句話說,使用者不能發送和訂閱臨時消息。
- 消息的類型需要在定義的時候指定。
- 定義消息的時候需要指定屬於哪個組件。
定好思路接下來來實現了
@ModuleEvents(module = "moduleA")
public class DemoEvents {
@ModuleType(Goods.class)
public static final String EVENT1 = "event1";
@ModuleType(User.class)
public static final String EVENT2 = "event2";
public static final String EVENT6 = "event6";
}
我們在extension模塊定義消息,我們將根據ModuleEvents和ModuleType兩個註解通過Processor生成EventDefineOfDemoEvents接口類,ModuleEvents註解是必須的,參數是module名,module名不是必須參數,如果不填,會通過gradle plugin獲取moduleName。ModuleType參數傳的消息實體。如果不填則實體是Object。
public interface EventDefineOfDemoEvents extends IEventsDefine {
Observable<Goods> EVENT1();
Observable<User> EVENT2();
Observable<Object> EVENT6();
}
生成的類是extension模塊暴露給其他模塊的接口。
//發送消息
EventBus
.get()
.of(EventDefineOfDemoEvents.class)
.EVENT1()
.post(new Goods("23423432"));
//訂閱消息
EventBus
.get()
.of(EventDefineOfDemoEvents.class)
.EVENT1()
.observe(this, goods -> {
Log.e("Main", "goods1111 is " + goods);
});
發送消息訂閱消息通過動態代理根據moduleName和eventName去二級map中查找到對應的LiveData。就可以observe了。
最後
如果你看到了這裏,覺得文章寫得不錯就給個讚唄!歡迎大家評論討論!如果你覺得那裏值得改進的,請給我留言。一定會認真查詢,修正不足,定期免費分享技術乾貨。感興趣的小夥伴可以點一下關注哦。謝謝!