Android項目組件化/模塊化,用LiveData打造屬於你組件通信框架EventBus

預備知識

  1. Android 基礎知識
  2. LiveData的基本定義和用法
  3. 開源消息框架EventBus的基本瞭解

看完本文能夠學到什麼

  1. Android項目組件化/模塊化
  2. 組件通信框架Router/EventBus的探究
  3. 用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是一樣的,在單項目下也堅挺了一段時間。後面項目完全組件化了,暴露出來的問題越來越大。

  1. 消息容易重名,因爲各個module都是有自己的研發團隊管理的,開發者對自己的模塊是比較熟悉的,對於其他不負責的模塊是兩眼一抹黑,完全不瞭解了。且如果幾個業務模塊都需要收到消息,需要定一個統一的消息名,最好能夠下沉到Base庫,大家都能引用到,下沉這個事情我想大家內心是拒絕的。
  2. 消息沒有綁定Activity/fragment的生命週期,可能造成內存泄漏。
  3. 消息約束力太差,容易造成消息錯用濫用,特麼好的沒有學到,EventBus的毛病全學到了。

爲了解決以上痛點,借鑑了美團的消息框架,決定用LiveData來打造屬於你的EventBus:

  1. 消息名由moduleName + eventName組成,保證了唯一性,解決不同組件定義了重名消息的問題採用兩級HashMap的方式解決。第一級HashMap的構建以ModuleName作爲Key,第二級HashMap作爲Value;第二級HashMap以消息名稱EventName作爲Key,LiveData作爲Value。查找的時候先用組件名稱ModuleName在第一級HashMap中查找,如果找到則用消息名EventName在第二級HashName中查找。
  2. 使用LiveData構建消息總線具有生命週期感知能力,使用者不需要調用反註冊,相比EventBus和RxBus使用更爲方便,並且沒有內存泄漏風險。
  3. 消息總線框架增強以下約束:
  • 只能訂閱和發送在組件中預定義的消息。換句話說,使用者不能發送和訂閱臨時消息。
  • 消息的類型需要在定義的時候指定。
  • 定義消息的時候需要指定屬於哪個組件。

定好思路接下來來實現了

@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了。

 

最後

如果你看到了這裏,覺得文章寫得不錯就給個讚唄!歡迎大家評論討論!如果你覺得那裏值得改進的,請給我留言。一定會認真查詢,修正不足,定期免費分享技術乾貨。感興趣的小夥伴可以點一下關注哦。謝謝!

 

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