天天看點

EventBus3.0實作發送事件後再注冊依然能擷取到事件的效果

時間過得很快,轉眼迎來了清明小長假,在小長假到來前期搞定了一個問題,心裡還是很高興的,這裡分享給大家,希望大家遇到相同問題時能有有所幫助。

先說一下我的需求吧,在手頭的項目裡有這麼一個需求,就是一個activity,它有三個fragment,activity要通過網絡從服務端擷取資料,然後分發給三個fragment展示。

最初,我是想用broadcastReceiver實作的,但是寫了一堆代碼以後發現不是想象的那麼簡單。當activity擷取到資料後發送廣播,此時FragmentA和FragmentB已經初始化完畢,當然也完成了BroadcastReceiver的注冊,這兩個是可以接受到activity傳遞過來的資料的,但是FragmentC就尴尬了,在activity發送廣播的時候它還沒有初始化,也就沒有注冊廣播接收器,自然也就沒有資料展示了。這個問題讓我很是頭大了一會兒,後來考慮的用一下EventBus試試,結果一試還真成了,哈哈哈哈,先讓我笑幾聲,然後在這裡記錄一下EventBus的使用吧。

首先,添加依賴庫:

Androidstudio的gradle配置如下:

compile 'org.greenrobot:eventbus:3.0.0'           
然後,定義一個MessageEvent類:
public class MessageEvent {
    private String message;

    public MessageEvent(String message) {
        this.message = message;
    }

    public String getMessage() {
        return message;
    }

    public void setMessage(String message) {
        this.message = message;
    }           
第三,在三個Fragment中注冊、訂閱事件:
public class FragmentA extends Fragment {
    private static final String TAG = "FragmentA";
    private TextView tvAAA;

    @Nullable
    @Override
    public View onCreateView(LayoutInflater inflater, @Nullable ViewGroup container, Bundle savedInstanceState) {
        View view=inflater.inflate(R.layout.fragment_aa,container,false);
        tvAAA=view.findViewById(R.id.text_a);

        Log.d(TAG, "onCreateView: ");
        //注冊
        EventBus.getDefault().register(this);

        return view;
    }

    @Override
    public void onDestroyView() {
        super.onDestroyView();
        Log.d(TAG, "onDestroyView: ");
        //登出
        EventBus.getDefault().unregister(this);
    }
}           
第四,事件訂閱者處理事件。這裡多說幾句,如果是普通的事件,無法實作剛開始說的事件先發送,後注冊擷取的效果,是以這裡使用的是EventBus 的 黏性事件,這個可以非常好的達到我們的要求。
//事件訂閱者處理事件,必須有此方法
    //這裡我們的ThreadMode設定為MAIN,事件的處理會在UI線程中執行,用TextView來展示收到的事件消息
    //注意,這裡的注解裡必須有sticky,并将值設定為true,表示與事件綁定,可以在事件已經發送但是還沒有注冊的情況下,一旦注冊,就可以接受到資料
    @Subscribe(threadMode = ThreadMode.MAIN,sticky = true)
    public void onResult(MessageEvent messageEvent){
        tvAAA.setText(messageEvent.getMessage());
    }
           
最後,發送黏性事件:
private void sendMsg() {
        new Thread(new Runnable() {
            @Override
            public void run() {
                try {
                    Thread.sleep(3000);
                    runOnUiThread(new Runnable() {
                        @Override
                        public void run() {

                            EventBus.getDefault().postSticky(new MessageEvent("here is activity"));

                        }
                    });
                } catch (Exception e) {
                    e.printStackTrace();
                }
            }
        }).start();
    }           

簡單的幾行代碼就實作了我們的要求是不是感覺so easy,不過效果雖然實作了,但是我們真正懂得了EventBus了嗎,未必!下面大概了解一下EventBus:

EventBus的三要素

EventBus有三個主要的元素需要我們先了解一下:

  • Event:事件,可以是任意類型的對象。
  • Subscriber:事件訂閱者,在EventBus3.0之前消息處理的方法隻能限定于onEvent、onEventMainThread、onEventBackgroundThread和onEventAsync,他們分别代表四種線程模型。而在EventBus3.0之後,事件處理的方法可以随便取名,但是需要添加一個注解@Subscribe,并且要指定線程模型(預設為POSTING),四種線程模型下面會講到。
  • Publisher:事件釋出者,可以在任意線程任意位置發送事件,直接調用EventBus的post(Object)方法。可以自己執行個體化EventBus對象,但一般使用EventBus.getDefault()就好了,根據post函數參數的類型,會自動調用訂閱相應類型事件的函數。

EventBus的四種ThreadMode(線程模型)

EventBus3.0有以下四種ThreadMode:

  • POSTING(預設):如果使用事件處理函數指定了線程模型為POSTING,那麼該事件在哪個線程釋出出來的,事件處理函數就會在這個線程中運作,也就是說釋出事件和接收事件在同一個線程。線上程模型為POSTING的事件處理函數中盡量避免執行耗時操作,因為它會阻塞事件的傳遞,甚至有可能會引起ANR。
  • MAIN: 

    事件的處理會在UI線程中執行。事件處理時間不能太長,長了會ANR的。

  • BACKGROUND:如果事件是在UI線程中釋出出來的,那麼該事件處理函數就會在新的線程中運作,如果事件本來就是子線程中釋出出來的,那麼該事件處理函數直接在釋出事件的線程中執行。在此事件處理函數中禁止進行UI更新操作。
  • ASYNC:無論事件在哪個線程釋出,該事件處理函數都會在建立的子線程中執行,同樣,此事件處理函數中禁止進行UI更新操作。

好了,就記錄這麼多吧,學海無涯!!!