安卓高級EventBus使用詳解

我原本想寫可是在網上看了下感受寫得不如此做者寫得好:http://www.jianshu.com/p/da9e193e8b03css

前言:EventBus出來已經有一段時間了,github上面也有不少開源項目中使用了EventBus。因此抽空學習順便整理了一下。目前EventBus最新版本是3.0,因此本文是基於EventBus3.0的。html

相關文章
EventBus使用詳解
EventBus源碼解析 java

概述

EventBus是針一款對Android的發佈/訂閱事件總線。它可讓咱們很輕鬆的實如今Android各個組件之間傳遞消息,而且代碼的可讀性更好,耦合度更低。android

如何使用

(1)首先須要定義一個消息類,該類能夠不繼承任何基類也不須要實現任何接口。如:git

public class MessageEvent {
    ......
}

(2)在須要訂閱事件的地方註冊事件github

EventBus.getDefault().register(this);

(3)產生事件,即發送消息服務器

EventBus.getDefault().post(messageEvent);

(4)處理消息網絡

@Subscribe(threadMode = ThreadMode.PostThread)
public void XXX(MessageEvent messageEvent) {
    ...
}

在3.0以前,EventBus尚未使用註解方式。消息處理的方法也只能限定於onEvent、onEventMainThread、onEventBackgroundThread和onEventAsync,分別表明四種線程模型。而在3.0以後,消息處理的方法能夠隨便取名,可是須要添加一個註解@Subscribe,而且要指定線程模型(默認爲PostThread),四種線程模型,下面會講到。
注意,事件處理函數的訪問權限必須爲public,不然會報異常。ide

(5)取消消息訂閱函數

EventBus.getDefault().unregister(this);

有何優勢

採用消息發佈/訂閱的一個很大的優勢就是代碼的簡潔性,而且可以有效地下降消息發佈者和訂閱者之間的耦合度。
舉個例子,好比有兩個界面,ActivityA和ActivityB,從ActivityA界面跳轉到ActivityB界面後,ActivityB要給ActivityA發送一個消息,ActivityA收到消息後在界面上顯示出來。咱們最早想到的方法就是使用廣播,使用廣播實現此需求的代碼以下:
首先須要在ActivityA中定義一個廣播接收器:

public class MessageBroadcastReceiver extends BroadcastReceiver {

    @Override
    public void onReceive(Context context, Intent intent) {
        mMessageView.setText("Message from SecondActivity:" + intent.getStringExtra("message"));
    }
}

還須要在onCreate()方法中註冊廣播接收器:

@Override
protected void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    setContentView(R.layout.activity_main);
    //註冊事件
    EventBus.getDefault().register(this);
    //註冊廣播
    IntentFilter intentFilter = new IntentFilter("message_broadcast");
    mBroadcastReceiver = new MessageBroadcastReceiver();
    registerReceiver(mBroadcastReceiver, intentFilter);
    ......
}

而後在onDestory()方法中取消註冊廣播接收器:

@Override
protected void onDestroy() {
    super.onDestroy();
    ......
    //取消廣播註冊
    unregisterReceiver(mBroadcastReceiver);
}

最後咱們須要在ActivityB界面中發送廣播消息:

findViewById(R.id.send_broadcast).setOnClickListener(new View.OnClickListener() {
    @Override
    public void onClick(View v) {
        String message = mMessageET.getText().toString();
        if(TextUtils.isEmpty(message)) {
            message = "defaule message";
        }
        Intent intent = new Intent();
        intent.setAction("message_broadcast");
        intent.putExtra("message", message);
        sendBroadcast(intent);
    }
});

看着上面的實現代碼,感受也沒什麼不妥,挺好的!下面對比看下使用EventBus如何實現。
根據文章最前面所講的EventBus使用步驟,首先咱們須要定義一個消息事件類:

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;
    }
}

在ActivityA界面中咱們首先須要註冊訂閱事件:

@Override
protected void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    setContentView(R.layout.activity_main);
    //註冊事件
    EventBus.getDefault().register(this);
    ......
}

而後在onDestory()方法中取消訂閱:

@Override
protected void onDestroy() {
    super.onDestroy();
    //取消事件註冊
    EventBus.getDefault().unregister(this);
}

固然還要定義一個消息處理的方法:

@Subscribe(threadMode = ThreadMode.MainThread)
public void onShowMessageEvent(MessageEvent messageEvent) {
    mMessageView.setText("Message from SecondActivity:" + messageEvent.getMessage());
}

至此,消息訂閱者咱們已經定義好了,咱們還須要在ActivityB中發佈消息:

findViewById(R.id.send).setOnClickListener(new View.OnClickListener() {
    @Override
    public void onClick(View v) {
        String message = mMessageET.getText().toString();
        if(TextUtils.isEmpty(message)) {
            message = "defaule message";
        }
        EventBus.getDefault().post(new MessageEvent(message));
    }
});

對比代碼一看,有人會說了,這尼瑪有什麼區別嘛!說好的簡潔呢?哥們,彆着急嘛!我這裏只是舉了個簡單的例子,僅僅從該例子來看,EventBus的優點沒有體現出來。如今我將需求稍微改一下,ActivityA收到消息後,須要從網絡服務器獲取數據並將數據展現出來。若是使用廣播,ActivityA中廣播接收器代碼應該這麼寫:

public class MessageBroadcastReceiver extends BroadcastReceiver {

    @Override
    public void onReceive(Context context, Intent intent) {
        new Thread(new Runnable() {
            @Override
            public void run() {
                //從服務器上獲取數據
                ......
                runOnUiThread(new Runnable() {
                    @Override
                    public void run() {
                        //將獲取的數據展現在界面上
                        ......
                    }
                });
            }
        }).start();
    }
}

看到這段代碼,不知道你何感想,反正我是看着很不爽,嵌套層次太多,徹底違反了Clean Code的原則。那使用EventBus來實現又是什麼樣呢?咱們看一下。

@Subscribe(threadMode = ThreadMode.BackgroundThread)
public void onGetDataEvent(MessageEvent messageEvent) {
    //從服務器上獲取數據
    ......
    EventBus.getDefault().post(new ShowMessageEvent());
}

@Subscribe(threadMode = ThreadMode.MainThread)
public void onShowDataEvent(ShowMessageEvent showMessageEvent) {
    //將獲取的數據展現在界面上
    ......
}

對比一下以上兩段代碼就能很明顯的感受到EventBus的優點,代碼簡潔、層次清晰,大大提升了代碼的可讀性和可維護性。我這只是簡單的加了一個小需求而已,隨着業務愈來愈複雜,使用EventBus的優點越發明顯。

經常使用API介紹

線程模型

在EventBus的事件處理函數中須要指定線程模型,即指定事件處理函數運行所在的想線程。在上面咱們已經接觸到了EventBus的四種線程模型。那他們有什麼區別呢?
在EventBus中的觀察者一般有四種線程模型,分別是PostThread(默認)、MainThread、BackgroundThread與Async。

  • PostThread:若是使用事件處理函數指定了線程模型爲PostThread,那麼該事件在哪一個線程發佈出來的,事件處理函數就會在這個線程中運行,也就是說發佈事件和接收事件在同一個線程。在線程模型爲PostThread的事件處理函數中儘可能避免執行耗時操做,由於它會阻塞事件的傳遞,甚至有可能會引發ANR。
  • MainThread:若是使用事件處理函數指定了線程模型爲MainThread,那麼不論事件是在哪一個線程中發佈出來的,該事件處理函數都會在UI線程中執行。該方法能夠用來更新UI,可是不能處理耗時操做。
  • BackgroundThread:若是使用事件處理函數指定了線程模型爲BackgroundThread,那麼若是事件是在UI線程中發佈出來的,那麼該事件處理函數就會在新的線程中運行,若是事件原本就是子線程中發佈出來的,那麼該事件處理函數直接在發佈事件的線程中執行。在此事件處理函數中禁止進行UI更新操做。
  • Async:若是使用事件處理函數指定了線程模型爲Async,那麼不管事件在哪一個線程發佈,該事件處理函數都會在新建的子線程中執行。一樣,此事件處理函數中禁止進行UI更新操做。

爲了驗證以上四個方法,我寫了個小例子。

@Subscribe(threadMode = ThreadMode.PostThread) public void onMessageEventPostThread(MessageEvent messageEvent) {
    Log.e("PostThread", Thread.currentThread().getName());
}

@Subscribe(threadMode = ThreadMode.MainThread) public void onMessageEventMainThread(MessageEvent messageEvent) {
    Log.e("MainThread", Thread.currentThread().getName());
}

@Subscribe(threadMode = ThreadMode.BackgroundThread) public void onMessageEventBackgroundThread(MessageEvent messageEvent) {
    Log.e("BackgroundThread", Thread.currentThread().getName());
}

@Subscribe(threadMode = ThreadMode.Async) public void onMessageEventAsync(MessageEvent messageEvent) {
    Log.e("Async", Thread.currentThread().getName());
}

分別使用上面四個方法訂閱同一事件,打印他們運行所在的線程。首先咱們在UI線程中發佈一條MessageEvent的消息,看下日誌打印結果是什麼。

findViewById(R.id.send).setOnClickListener(new View.OnClickListener() {
        @Override
        public void onClick(View v) {
            Log.e("postEvent", Thread.currentThread().getName());
            EventBus.getDefault().post(new MessageEvent());
        }
    });

打印結果以下:

2689-2689/com.lling.eventbusdemo E/postEvent﹕ main
2689-2689/com.lling.eventbusdemo E/PostThread﹕ main
2689-3064/com.lling.eventbusdemo E/Async﹕ pool-1-thread-1
2689-2689/com.lling.eventbusdemo E/MainThread﹕ main
2689-3065/com.lling.eventbusdemo E/BackgroundThread﹕ pool-1-thread-2

從日誌打印結果能夠看出,若是在UI線程中發佈事件,則線程模型爲PostThread的事件處理函數也執行在UI線程,與發佈事件的線程一致。線程模型爲Async的事件處理函數執行在名字叫作pool-1-thread-1的新的線程中。而MainThread的事件處理函數執行在UI線程,BackgroundThread的時間處理函數執行在名字叫作pool-1-thread-2的新的線程中。

咱們再看看在子線程中發佈一條MessageEvent的消息時,會有什麼樣的結果。

findViewById(R.id.send).setOnClickListener(new View.OnClickListener() {
        @Override
        public void onClick(View v) {
            new Thread(new Runnable() {
                @Override
                public void run() {
                    Log.e("postEvent", Thread.currentThread().getName());
                    EventBus.getDefault().post(new MessageEvent());
                }
            }).start();
        }
    });

打印結果以下:

3468-3945/com.lling.eventbusdemo E/postEvent﹕ Thread-125
3468-3945/com.lling.eventbusdemo E/PostThread﹕ Thread-125
3468-3945/com.lling.eventbusdemo E/BackgroundThread﹕ Thread-125
3468-3946/com.lling.eventbusdemo E/Async﹕ pool-1-thread-1
3468-3468/com.lling.eventbusdemo E/MainThread﹕ main

從日誌打印結果能夠看出,若是在子線程中發佈事件,則線程模型爲PostThread的事件處理函數也執行在子線程,與發佈事件的線程一致(都是Thread-125)。BackgroundThread事件模型也與發佈事件在同一線程執行。Async則在一個名叫pool-1-thread-1的新線程中執行。MainThread仍是在UI線程中執行。

上面一個例子充分驗證了指定不一樣線程模型的事件處理方法執行所在的線程。

黏性事件

除了上面講的普通事件外,EventBus還支持發送黏性事件。何爲黏性事件呢?簡單講,就是在發送事件以後再訂閱該事件也能收到該事件,跟黏性廣播相似。具體用法以下:

訂閱黏性事件:

EventBus.getDefault().register(StickyModeActivity.this);

黏性事件處理函數:

@Subscribe(sticky = true)
public void XXX(MessageEvent messageEvent) {
    ......
}

發送黏性事件:

EventBus.getDefault().postSticky(new MessageEvent("test"));

處理消息事件以及取消訂閱和上面方式相同。

看個簡單的黏性事件的例子,爲了簡單起見我這裏就在一個Activity裏演示了。

Activity代碼:

public class StickyModeActivity extends AppCompatActivity {

    int index = 0;
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_sticky_mode);
        findViewById(R.id.post).setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                EventBus.getDefault().postSticky(new MessageEvent("test" + index++));
            }
        });
        findViewById(R.id.regist).setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                EventBus.getDefault().registerSticky(StickyModeActivity.this);
            }
        });

        findViewById(R.id.unregist).setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                EventBus.getDefault().unregister(StickyModeActivity.this);
            }
        });

    }

    @Subscribe(threadMode = ThreadMode.PostThread, sticky = true)
    public void onMessageEventPostThread(MessageEvent messageEvent) {
        Log.e("PostThread", messageEvent.getMessage());
    }

    @Subscribe(threadMode = ThreadMode.MainThread, sticky = true)
    public void onMessageEventMainThread(MessageEvent messageEvent) {
        Log.e("MainThread", messageEvent.getMessage());
    }

    @Subscribe(threadMode = ThreadMode.BackgroundThread, sticky = true)
    public void onMessageEventBackgroundThread(MessageEvent messageEvent) {
        Log.e("BackgroundThread", messageEvent.getMessage());
    }

    @Subscribe(threadMode = ThreadMode.Async, sticky = true)
    public void onMessageEventAsync(MessageEvent messageEvent) {
        Log.e("Async", messageEvent.getMessage());
    }

}

佈局代碼activity_sticky_mode.xml:

<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" xmlns:tools="http://schemas.android.com/tools" android:layout_width="match_parent" android:layout_height="match_parent" android:paddingLeft="@dimen/activity_horizontal_margin" android:paddingRight="@dimen/activity_horizontal_margin" android:paddingTop="@dimen/activity_vertical_margin" android:paddingBottom="@dimen/activity_vertical_margin" android:orientation="vertical" tools:context="com.lling.eventbusdemo.StickyModeActivity">

    <Button android:id="@+id/post" android:layout_width="wrap_content" android:layout_height="wrap_content" android:text="Post"/>

    <Button android:id="@+id/regist" android:layout_width="wrap_content" android:layout_height="wrap_content" android:text="Regist"/>

    <Button android:id="@+id/unregist" android:layout_width="wrap_content" android:layout_height="wrap_content" android:text="UnRegist"/>

</LinearLayout>

代碼很簡單,界面上三個按鈕,一個用來發送黏性事件,一個用來訂閱事件,還有一個用來取消訂閱的。首先在未訂閱的狀況下點擊發送按鈕發送一個黏性事件,而後點擊訂閱,會看到日誌打印結果以下:

15246-15246/com.lling.eventbusdemo E/PostThread﹕ test0
15246-15391/com.lling.eventbusdemo E/Async﹕ test0
15246-15246/com.lling.eventbusdemo E/MainThread﹕ test0
15246-15393/com.lling.eventbusdemo E/BackgroundThread﹕ test0

這就是粘性事件,可以收到訂閱以前發送的消息。可是它只能收到最新的一次消息,好比說在未訂閱以前已經發送了多條黏性消息了,而後再訂閱只能收到最近的一條消息。這個咱們能夠驗證一下,咱們連續點擊5次POST按鈕發送5條黏性事件,而後再點擊REGIST按鈕訂閱,打印結果以下:

6980-6980/com.lling.eventbusdemo E/PostThread﹕ test4
6980-6980/com.lling.eventbusdemo E/MainThread﹕ test4
6980-7049/com.lling.eventbusdemo E/Async﹕ test4
6980-7048/com.lling.eventbusdemo E/BackgroundThread﹕ test4

由打印結果能夠看出,確實是只收到最近的一條黏性事件。

好了,EventBus的使用暫時分析到這裏,例子代碼從這裏獲取。下一講將講解EventBus的源碼。

本文首發:http://liuling123.com/2016/01/EventBus-explain.html



文/Lauren_Liuling(簡書做者) 原文連接:http://www.jianshu.com/p/da9e193e8b03 著做權歸做者全部,轉載請聯繫做者得到受權,並標註「簡書做者」。
相關文章
相關標籤/搜索