前言
EventBus是greenrobot再Android平臺發佈的以訂閱-發佈模式爲核心的開源庫。
EventBus翻譯過來是事件總線意思。可以這樣理解:一個個(event)發送到總線上,
然後EventBus根據已註冊的訂閱者(subscribers)來匹配相應的事件,進而把事件傳遞給訂閱者,
這也是觀察者模式的一個最佳實踐。
我們平常開發中,當遇到Activity與Activity、Activity與Fragment之間的通信,往往採用intent,又
或者線程之間用Handler進行通信,這樣代碼會複雜很多,而使用EventBus極大簡化兩個組件之間俺的通信問題,
而且效率極高。而EventBus升級到3.0版本後,開發者能夠自定義訂閱方法名字,而沒必要
規定以“o’n’Event’XX”開頭的方法了,這樣也自由化了很多,而且支持了粘性事件的分發等,因此學會使用EventBus3.0
對我們開發又極大的好處.
例子
佈局
activity_main
<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/app_name"
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>
activity_second
<?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">
<EditText
android:id="@+id/et"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:hint="請輸入要發送的消息"
/>
<Button
android:id="@+id/sendMessageBtn"
android:text="發送消息"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
/>
</LinearLayout>
代碼
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);
}
}
public class SecondActivity extends Activity {
private EditText et;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_second);
et = findViewById(R.id.et);
Button button = (Button) findViewById(R.id.sendMessageBtn);
button.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
if(TextUtils.isEmpty(et.getText().toString())){
Toast.makeText(SecondActivity.this, "請輸入......", Toast.LENGTH_SHORT).show();
return;
}else {
EventBus.getDefault().post(new MessageEvent(et.getText().toString()));
finish();
}
}
});
}
}
效果圖
發送黏性事件Sticky Events
上面示例代碼所說的情況是:當發送消息推送者推送消息的時候,訂閱者會立馬收到消息,它會把消息推送給它所有的訂閱者.注意後面這句話:如果你希望在消息推送完成之後,讓新註冊的訂閱者也能收到這條消息,這時候你可以試試Sticky Events,這個事件就像一個常駐廣播,只要是有新的訂閱者訂閱了這個事件,就會收到消息.當然,有兩點要求:
1.首先,發送的是黏性事件,代碼將post改爲postSticky
// EventBus.getDefault().post(new MessageEvent());
EventBus.getDefault().postSticky(new MessageEvent());
2.然後,訂閱者要聲明自己能夠接收到黏性事件的消息:代碼中@Subscribe註解中的sticky值爲true,滿足了這兩點,就能愉快的玩耍了.
@Subscribe(sticky = true)
public void onMessageEvent(MessageEvent event) {
Log.i(TAG, "onMessageEvent: 我是sticky event 收到消息");
}
3.測試效果
把項目入口調整爲ThridActivity
public class ThirdActivity extends Activity implements View.OnClickListener {
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_third);
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 view) {
switch (view.getId()){
case R.id.sendStickyMessageBtn1:
EventBus.getDefault().postSticky(new MessageEvent("粘性事件1"));
Log.e("cylog","發送粘性事件1...");
break;
case R.id.sendStickyMessageBtn2:
EventBus.getDefault().postSticky(new MessageEvent("粘性事件2"));
Log.e("cylog", "發送粘性事件2...");
break;
case R.id.sendStickyMessageBtn3:
EventBus.getDefault().postSticky(new MessageEvent("粘性事件3"));
Log.e("cylog", "發送粘性事件3...");
break;
case R.id.sendRegisterBtn:
Log.e("cylog", "註冊成爲訂閱者...");
EventBus.getDefault().register(this);
break;
}
}
@Subscribe(sticky = true)
public void onEvent(MessageEvent messageEvent){
Log.e("cylog","接受到了來自EventBus的事件:"+messageEvent.getMessage());
}
@Override
protected void onDestroy() {
super.onDestroy();
EventBus.getDefault().unregister(this);
}
}
那麼很明顯,只會接受到最後發送的粘性事件,在此之前的事件都接收不到。
線程模型
在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());
}
});
從日誌打印結果可以看出,如果在UI線程中發佈事件,則線程模型爲PostThread的事件處理函數也執行在UI線程,與發佈事件的線程一致。線程模型爲Async的事件處理函數執行在名字叫做pool-1-thread-1的新的線程中。而MainThread的事件處理函數執行在UI線程,BackgroundThread的時間處理函數執行在名字叫做pool-1-thread-2的新的線程中。
我們再看看在子線程中發佈一條MessageEvent的消息時,會有什麼樣的結果。
從日誌打印結果可以看出,如果在子線程中發佈事件,則線程模型爲PostThread的事件處理函數也執行在子線程,與發佈事件的線程一致(都是Thread-125)。BackgroundThread事件模型也與發佈事件在同一線程執行。Async則在一個名叫pool-1-thread-1的新線程中執行。MainThread還是在UI線程中執行。
上面一個例子充分驗證了指定不同線程模型的事件處理方法執行所在的線程。