Android EventBus開源項目

jopen 10年前發布 | 57K 次閱讀 EventBus Android開發 移動開發

場景描述</strong>
當一個Android應用功能越來越多的時候,保證應用的各個部分之間高效的通信將變得越來越困難。
在應用中的多個地方,控件經常需要根據某個狀態來更新他們顯示的內容。這種場景常見的解決方式就是定義一個接口,需要關注該事件的控件來實現這個接口。然后事件觸發的地方來注冊/取消注冊這些對該事件感興趣的控件。
</span>

例如,陌陌依賴手機位置信息來獲取附近的用戶,所以在位置更新管理器(MmLocationManager)中定義了一個接口來監聽位置更新的事件(MmLocationListener):

    interface MmLocationListener {  
     void onLocationChanged(Location location);  
    }  

然后在應用的各個需要響應該事件的地方來實現上面的接口,然后在位置更新管理器(MmLocationManager)中注冊/取消注冊事件監聽接口的實現類:

  1. mLocationManager.get().register(this);  
  2. </ol> </div>
    當不在需要監聽的時候,取消注冊:

     
        mLocationManager.get().unregister(this);  

    問題
    上面的解決方案是沒問題的,但是不是理想方案。每個控件實現這個接口,導致這些控件和位置管理器注冊強耦合在一起。這還意味著,當單元測試的時候,您需要模擬(mocked)位置管理器來生成位置更新事件。


    隨著應用功能的增加,需要監聽的事件越來越多,而越來越多的控件需要監聽不同的事件,則導致越來越多的控件需要注冊到各種事件管理器上:

    </div> </div>

        // 代碼開始變得無法控制…  
        mLocationManager.get().register(this);  
        userAuthenticator.get().register(this);  
        settingsManager.get().register(this);  
        syncManager.get().register(this);  
        configurationMonitor.get().register(this);  

    注意:上面的每個事件的注冊,都要實現對應的時間更新接口。

    注冊和取消注冊這些事件慢慢的會變得越來越難以管理。導致測試越來越困難,并將導致開發者的效率越來越低,同時在您的應用中越來越容易引入各種奇怪的Bug。

    解決方案
    為了找出該問題的優雅解決方案,從一個意想不到的的地方借鑒點經驗 — Swing應用。 Event Bus模式 — 也被稱為Message Bus或者發布者/訂閱者(publisher/subscriber)模式 — 可以讓兩個組件相互通信,但是他們之間并不相互知曉。

    和需要注冊各個事件的監聽器相比,一個組件現在只用在Event Bus上注冊一次即可:

     
        bus.register(this);  

    上面的注冊告訴Event Bus我們現在希望接收各個事件的更新。 然后Bus檢測該類中每個帶有@Subscribe注解的函數,當相關的事件發生的時候就調用這些帶有注解的函數。
    上面示例中的位置監聽功能,不用實現位置監聽接口和里面的函數了,只需要提供一個帶有@Subscribe注解的函數即可:

        @Subscribe  
        public void locationChanged(LocationChangedEvent event) {  
           // TODO React to location change.  
        }  
    </div> </div>
    現在Event Bus會把所有的LocationChangedEvent 事件都發送給上面的函數。MmLocationManager 類不用注冊監聽器了,當位置改變的時候 只需要向Event Bus發布事件即可:

     
        bus.post(new LocationChangedEvent(37.892818, -121.772608));  


    這樣 組件間相互解耦了,而單元測試也變得簡單了。任何事件都可以發布給Event Bus,然后Event Bus會找到對該事件感興趣的函數來調用。
    注意:您也許已經發現該模式在Android上層也存在 — Intent系統就是這樣設計的!

     

     

    Android系統的Event Bus模式類庫

    1、Otto — Android系統的Event Bus類庫
    Otto是Square公司在他們應用中使用的Event Bus實現。從Guava中演變而來,并且專注于Android平臺。
    通過使用Otto,Square公司的應用組件間不緊密耦合了,單元測試也更加容易了。
    Otto項目的主頁:http://square.github.io/otto/

    Otto項目GitHub地址:https://github.com/square/otto

    2、EventBus — Android系統的Event Bus類庫
    EventBus 是http://greenrobot.de 出品的另外一個Event Bus類庫,功能稍微多一點。


    EventBus 項目地址:https://github.com/greenrobot/EventBus


    關于兩者的對比可以參考EventBus 項目Comparison with Square's Otto部分: https://github.com/greenrobot/EventBus#comparison-with-squares-otto



    EventBus介紹
    EventBus可以向不同的線程中發布事件,在ThreadMode 枚舉中定義了4個線程,只需要在事件響應函數名稱“onEvent”后面添加對應的線程類型名稱,則還事件響應函數就會在對應的線程中執行,比如事件函數 “onEventAsync”就會在另外一個異步線程中執行,ThreadMode定義的4個線程類型如下:
    PostThread:事件響應函數和事件發布在同一線程中執行。這個是默認值,這樣可以避免線程切換。
    MainThread:事件響應函數會在Android應用的主線程(大部分情況下都是UI線程)中執行。
    BackgroundThread:事件響應函數會在一個后臺線程中執行。如果事件發布函數不是在主線程中,則會立即在事件發布線程中執行響應函數。如果事件發布函數在主線程中,EventBus則會在唯一的一個后臺線程中按照順序來執行所有的后臺事件響應函數。
    上面的3種事件響應函數,應該能夠很快的執行完,不然的話會阻塞各自的事件發布。
    async:事件響應函數在另外一個異步線程中執行。該線程和發布線程、主線程相互獨立。如果事件響應函數需要較長的時間來執行,則應該使用該模式,例如 網絡訪問等。需要注意的是,由于系統并行的限制,應該避免在同一時間觸發大量的異步線程。 


    在Android中我們可以在onCreate方法中調用EventBus的register(Object subscriber) 注冊訂閱者:

    </div> </div>

        @Override
    protected void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    setContentView(R.layout.activity_main);

            findView();  
    
            EventBus.getDefault().register(this);  
        }  </pre><br />
    


    調用EventBus的unregister(Object subscriber) 方法 取消注冊的訂閱者:

    </div> </div>

        @Override  
            protected void onDestroy() {  
                super.onDestroy();  
                try {  
                    EventBus.getDefault().unregister(this);  
                } catch (Throwable t){  
                    //this may crash if registration did not go through. just be safe  
                }  
            }  

    調用EventBus的post(Object event) 方法 post消息到Bus上,本例是在一個子線程中post一個消息到UI線程:

    </div> </div>

        @Override  
            public void onRun() throws Throwable {  
                // TODO Auto-generated method stub  
                Log.e(TAG, "onRun sleep 6s");  
                Thread.sleep(6000);  
                EventBus.getDefault().post(new PostedCityEvent("posted"));  
            }  

    在訂閱者中實現的 EventBus 上的事件處理方法,本例是在Activity中進行處理的:

    </div> </div>

        public void onEventMainThread(PostedCityEvent ignored) {

            Log.i(TAG, "PostedCityEvent status="+ignored.getStatus());  
        }  </pre><br />
    


    注意事件處理方法命名都是 onEventxxx,其中xxx表示線程類型,如本例中線程類型是MainThread。




    具體使用例子可以參考:http://awalkingcity.com/blog/2013/02/26/productive-android-eventbus/

     本文由用戶 jopen 自行上傳分享,僅供網友學習交流。所有權歸原作者,若您的權利被侵害,請聯系管理員。
     轉載本站原創文章,請注明出處,并保留原始鏈接、圖片水印。
     本站是一個以用戶分享為主的開源技術平臺,歡迎各類分享!