前言
EventBus是greenrobot在Android平臺發布的一款以訂閱——發布模式為核心的開源庫。EventBus翻譯過來是事件總線的意思,可以這樣理解:一個個事件(event)發送到總線上,然后EventBus根據已注冊的訂閱者(subscribers)來匹配相應的事件,進而把事件傳遞給訂閱者,這也是觀察者模式的一個最佳實踐。
那么EventBus可以用到什么地方呢?我們平時開發的時候,當遇到Activity與Activity、Activity與Fragment之間的通信,往往采用Intent,又或者線程之間使用Handler進行通信,這樣代碼難免會復雜許多,而使用EventBus能極大簡化兩個組件之間的通信問題,而且效率極高,而EventBus升級到3.0版本后,開發者能夠自定義訂閱方法的名字,而沒必要規定以“onEventXX”開頭的方法了,這樣也自由化了很多,而且支持了粘性事件的分發等,因此學會使用EventBus3.0對我們的開發有極大的好處。
官網
EventBus GitHub地址
在Android Studio中添加如下依賴:
compile 'org.greenrobot:eventbus:3.0.0'
使用
在準備好EventBus后,我們通過一個例子來展示它的使用方法。實現的例子很簡單,主要是在Activity1中打開Activity2,而Activity2中通過點擊按鈕來使Activity1中的TextView顯示內容得到更改,而顯示的內容由Activity2指定。這便構成了一個典型的Activity與Activity之間通信的場景,如果用我們之前的做法可以采用BroadcastReceiver來實現兩者的通信,但現在我們使用EventBus,只需要幾行代碼就能實現了。
首先,我們準備兩個布局文件,分別是MainActivity和SecondActivity的:
activity_main.xml:
<RelativeLayout 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" >
<TextView
android:id="@+id/tv_text"
android:textSize="20sp"
android:text="@string/hello_world"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_centerInParent="true"/>
<Button
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="點擊打開新的Activity"
android:id="@+id/secondActivityBtn"
android:layout_alignParentBottom="true"
android:layout_centerHorizontal="true"
android:layout_marginBottom="75dp" />
</RelativeLayout>
second_main.xml:
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:orientation="vertical"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:gravity="center">
<Button
android:id="@+id/sendMessageBtn"
android:text="發送消息"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
/>
</LinearLayout>
接著,我們利用EventBus來實現上述需求。
Step 1.創建事件實體類
所謂的事件實體類,就是傳遞的事件,一個組件向另一個組件發送的信息可以儲存在一個類中,該類就是一個事件,會被EventBus發送給訂閱者。新建MessageEvent.java:
public class MessageEvent {
private String message;
public MessageEvent(String message){
this.message = message;
}
public String getMessage(){
return message;
}
}
這里創建的事件非常簡單,只是為了示例需要。
Step 2.向EventBus注冊,成為訂閱者以及解除注冊
通過以下代碼:
EventBus.getDefault().register(this);
即可將當前類注冊,成為訂閱者,即對應觀察者模式的“觀察者”,一旦有事件發送過來,該觀察者就會接收到匹配的事件。通常,在類的初始化時便進行注冊,如果是Activity則在onCreate()方法內進行注冊。
當訂閱者不再需要接受事件的時候,我們需要解除注冊,釋放內存:
EventBus.getDefault().unregister(this);
Step 3.聲明訂閱方法
回想觀察者模式,觀察者有著一個update()方法,在接收到事件的時候會調用該update()方法,這個方法就是一個訂閱方法。在EventBus 3.0中,聲明一個訂閱方法需要用到@Subscribe注解,因此在訂閱者類中添加一個有著@Subscribe注解的方法即可,方法名字可自定義,而且必須是public權限,其方法參數有且只能有一個,另外類型必須為第一步定義好的事件類型(比如上面的MessageEvent),如下所示:
@Subscribe
public void onEvent(AnyEventType event) {
/* Do something */
}
完整的MainActivity.java文件如下所示:
public class MainActivity extends Activity {
private TextView textView;
private Button button;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
//注冊成為訂閱者
EventBus.getDefault().register(this);
textView = (TextView) findViewById(R.id.tv_text);
button = (Button) findViewById(R.id.secondActivityBtn);
button.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
Intent intent = new Intent(MainActivity.this, SecondActivity.class);
startActivity(intent);
}
});
}
//訂閱方法,當接收到事件的時候,會調用該方法
@Subscribe(threadMode = ThreadMode.MAIN)
public void onEvent(MessageEvent messageEvent){
Log.d("cylog","receive it");
textView.setText(messageEvent.getMessage());
Toast.makeText(MainActivity.this, messageEvent.getMessage(), Toast.LENGTH_SHORT).show();
}
@Override
protected void onDestroy() {
super.onDestroy();
//解除注冊
EventBus.getDefault().unregister(this);
}
}
Step 4.發送事件
與觀察者模式對應的,當有事件發生,需要通知觀察者的時候,被觀察者會調用notifyObservers()方法來通知所有已經注冊的觀察者,在EventBus中,對觀察者模式底層進行了封裝,我們只需要調用以下代碼就能把事件發送出去:
EventBus.getDefault().post(EventType eventType);
上述EventType就是第一步定義的事件類型。
SecondActivity.java代碼如下所示:
public class SecondActivity extends Activity {
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.second_main);
Button button = (Button) findViewById(R.id.sendMessageBtn);
button.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
EventBus.getDefault().post(new MessageEvent("Hello !....."));
}
});
}
}
由此可見,經過以上簡單的四個步驟,就能實現事件在組件之間的傳遞了,這是EventBus的便捷性。
進一步認識@Subscribe注解
我們回頭看看上面的訂閱方法,添加了@Subscribe注解,該注解標識了當前方法為訂閱方法,我們可以看到上面我們還給該注解賦值(threadMode = ThreadMode.MAIN),那么,這個代表了什么意思呢?首先,我們看看@Subscribe的類文件:
@Documented
@Retention(RetentionPolicy.RUNTIME)
@Target({ElementType.METHOD})
public @interface Subscribe {
ThreadMode threadMode() default ThreadMode.POSTING;
/**
* If true, delivers the most recent sticky event (posted with
* {@link EventBus#postSticky(Object)}) to this subscriber (if event available).
*/
boolean sticky() default false;
/** Subscriber priority to influence the order of event delivery.
* Within the same delivery thread ({@link ThreadMode}), higher priority subscribers will receive events before
* others with a lower priority. The default priority is 0. Note: the priority does *NOT* affect the order of
* delivery among subscribers with different {@link ThreadMode}s! */
int priority() default 0;
}
該注解內部有三個成員,分別是threadMode、sticky、priority。threadMode代表訂閱方法所運行的線程,sticky代表是否是粘性事件,priority代表優先級。給這個三個成員賦不同的值,能使得訂閱方法有著不同的效果。
1.ThreadMode是一個枚舉類型,有著以下幾個類型:
public enum ThreadMode {
POSTING,
MAIN,
BACKGROUND,
ASYNC
}
POSTING:表示訂閱方法運行在發送事件的線程。
MAIN:表示訂閱方法運行在UI線程,由于UI線程不能阻塞,因此當使用MAIN的時候,訂閱方法不應該耗時過長。
BACKGROUND:表示訂閱方法運行在后臺線程,如果發送的事件線程不是UI線程,那么就使用該線程;如果發送事件的線程是UI線程,那么新建一個后臺線程來調用訂閱方法。
ASYNC:訂閱方法與發送事件始終不在同一個線程,即訂閱方法始終會使用新的線程來運行。
ThreadMode默認是使用POSTING的,如果需要更改設置,可以在添加注解的時候同時為threadMode賦值。
2.priority 優先級
設置該優先級的目的是,當一個事件有多個訂閱者的時候,優先級高的會優先接收到事件。
3.sticky 粘性事件
關于粘性事件,可以參考Android的廣播機制,其中有一個粘性廣播,粘性廣播的意思是:該廣播發送后,會保存在內存中,如果后來有注冊的Receiver與之匹配,那么該Receiver便會接收到該廣播。那么粘性事件同理,在注冊之前便把事件發生出去,等到注冊之后便會收到最近發送的粘性事件(必須匹配)。注意:只會接收到最近發送的一次粘性事件,之前的會接受不到。為了測試,我們來建立一個小demo:
我們在Activity中添加四個按鈕,布局很簡單不給出了,前三個按鈕分別發送三個粘性事件,最后一個按鈕進行注冊,代碼如下:
public class SecondActivity extends Activity implements View.OnClickListener {
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.second_main);
Button button1 = (Button) findViewById(R.id.sendStickyMessageBtn1);
Button button2 = (Button) findViewById(R.id.sendStickyMessageBtn2);
Button button3 = (Button) findViewById(R.id.sendStickyMessageBtn3);
Button button4 = (Button) findViewById(R.id.sendRegisterBtn);
button1.setOnClickListener(this);
button2.setOnClickListener(this);
button3.setOnClickListener(this);
button4.setOnClickListener(this);
}
@Override
public void onClick(View v) {
switch (v.getId()){
case R.id.sendStickyMessageBtn1:
EventBus.getDefault().postSticky(new MessageEvent("粘性事件1"));
Log.d("cylog","發送粘性事件1...");
break;
case R.id.sendStickyMessageBtn2:
EventBus.getDefault().postSticky(new MessageEvent("粘性事件2"));
Log.d("cylog", "發送粘性事件2...");
break;
case R.id.sendStickyMessageBtn3:
EventBus.getDefault().postSticky(new MessageEvent("粘性事件3"));
Log.d("cylog", "發送粘性事件3...");
break;
case R.id.sendRegisterBtn:
Log.d("cylog", "注冊成為訂閱者...");
EventBus.getDefault().register(this);
break;
}
}
@Subscribe(sticky = true)
public void onEvent(MessageEvent messageEvent){
Log.d("cylog","接受到了來自EventBus的事件:"+messageEvent.getMessage());
}
@Override
protected void onDestroy() {
super.onDestroy();
EventBus.getDefault().unregister(this);
}
}
我們依次按下這4個按鈕,按先后次序發送粘性事件1、2、3以及注冊訂閱者,我們可以看到下面的結果:
那么很明顯,只會接受到最后發送的粘性事件,在此之前的事件都接收不到。
本篇文章到此為止,系統地介紹了EventBus 3.0的詳細用法,其中也穿插了觀察者模式的幾個知識點,如果對觀察者模式不熟悉的讀者可以查看我之前發的一篇文章。至于EventBus其背后的原理以及源碼將會在下一篇文章中繼續為大家詳細解析,謝謝大家的閱讀~