一、概念
EventBus是一款在 Android 開發(fā)中使用的發(fā)布-訂閱事件總線框架,基于觀察者模式,將事件的接收者和發(fā)送者解耦,簡化了組件之間的通信,使用簡單、效率高、體積小。
一句話:用于Android組件間通信的。
二、原理

三、簡單使用
- 在app module的builde.gradle文件中導入依賴庫:
implementation 'org.greenrobot:eventbus:3.3.1'
- 配置混淆
-keepattributes *Annotation*
-keepclassmembers class * {
@org.greenrobot.eventbus.Subscribe <methods>;
}
-keep enum org.greenrobot.eventbus.ThreadMode { *; }
# Only required if you use AsyncExecutor
-keepclassmembers class * extends org.greenrobot.eventbus.util.ThrowableFailureEvent {
<init>(java.lang.Throwable);
}
1、訂閱者EventBusService后臺注冊,前臺EventBusActivity 發(fā)送的數(shù)據。注冊以后一定要記得解注冊,否則會內存泄漏。onMsgEventReceived是接收消息的方法,該方法定義需要注意:
- 該方法有且僅有一個參數(shù);
- 必須用
public修飾,不能使用static或者abstract; - 需要添加
@Subscribe()注解;
public class EventBusService extends Service {
private static final String TAG = "Test_EventBusService";
@Override
public void onCreate() {
super.onCreate();
//注冊數(shù)據監(jiān)聽
EventBus.getDefault().register(this);
}
@Nullable
@Override
public IBinder onBind(Intent intent) {
return null;
}
@Subscribe
public void onMsgEventReceived(String msg) {
Log.i(TAG, "String msg: " + msg);
}
@Subscribe(threadMode = ThreadMode.MAIN, sticky = true, priority = 1)
public void onMsgEventReceived(MsgEvent event) {
Log.i(TAG, "MsgEvent msg: " + event.getMsg());
}
@Override
public void onDestroy() {
super.onDestroy();
//解注冊數(shù)據監(jiān)聽
EventBus.getDefault().unregister(this);
}
}
2、前臺Activity在按鈕點擊的時候發(fā)送信息到后臺Service。
public class EventBusActivity extends AppCompatActivity {
private static final String TAG = "Test_EventBusActivity";
@Override
protected void onCreate(@Nullable Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_event_bus);
Button msg1Btn = findViewById(R.id.btn1);
Button msg2Btn = findViewById(R.id.btn2);
msg1Btn.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View view) {
//發(fā)送數(shù)據給監(jiān)聽者
EventBus.getDefault().post("msg1 - coming!?。?);
}
});
msg2Btn.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View view) {
//發(fā)送數(shù)據給監(jiān)聽者
MsgEvent event = new MsgEvent("msg2 - coming?。。?);
EventBus.getDefault().post(event);
}
});
}
@Override
protected void onDestroy() {
super.onDestroy();
}
}
3、MsgEvent數(shù)據類型。
public class MsgEvent {
private String msg;
public MsgEvent(String msg) {
this.msg = msg;
}
public String getMsg() {
return msg;
}
public void setMsg(String msg) {
this.msg = msg;
}
@Override
public String toString() {
return "MsgEvent{" +
"msg='" + msg + '\'' +
'}';
}
}
4、運行結果

四、Subscribe注解
Subscribe是EventBus自定義的注解,共有三個參數(shù)(可選):ThreadMode、boolean sticky、int priority。
@Subscribe(threadMode = ThreadMode.MAIN, sticky = true, priority = 1)
public void onMsgEventReceived(MsgEvent event) {
Toast.makeText(this, event.getMsg(), Toast.LENGTH_LONG).show();
}
1、ThreadMode取值:
- ThreadMode.POSTING:默認的線程模式,在哪個線程發(fā)送事件就在對應線程處理事件。避免了線程切換,效率高。
代碼測試:
#EventBusActivity
msg1Btn.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View view) {
//發(fā)送數(shù)據給監(jiān)聽者
new Thread(new Runnable() {
@Override
public void run() {
Log.i(TAG, "post thread: " + Thread.currentThread().getName());
EventBus.getDefault().post("msg1 - coming?。?!");
}
}).start();
}
});
#EventBusService
@Subscribe
public void onMsgEventReceived(String msg) {
Log.i(TAG, "onMsgEventReceived thread: " + Thread.currentThread().getName());
Log.i(TAG, "String msg: " + msg);
}
把post的動作放到子線程中,結果如下,在哪個線程發(fā)送,就會在哪個線程執(zhí)行:

-
ThreadMode.MAIN:如在主線程(UI線程)發(fā)送事件,則直接在主線程處理事件;如果在子線程發(fā)送事件,則先將事件入隊列,然后通過
Handler切換到主線程,依次處理事件。
該模式下,在主線程(UI線程)發(fā)送事件,則直接在主線程處理事件,如果處理方法中有耗時操作就會堵塞進程。
代碼測試1:
#EventBusActivity
msg1Btn.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View view) {
//發(fā)送數(shù)據給監(jiān)聽者
new Thread(new Runnable() {
@Override
public void run() {
Log.i(TAG, "post thread: " + Thread.currentThread().getName());
EventBus.getDefault().post("msg1 - coming!??!");
}
}).start();
}
});
#EventBusService
@Subscribe(threadMode = ThreadMode.MAIN)
public void onMsgEventReceived(String msg) {
Log.i(TAG, "onMsgEventReceived thread: " + Thread.currentThread().getName());
Log.i(TAG, "String msg: " + msg);
}
發(fā)送post代碼放到子線程中,處理事件代碼加上ThreadMode.MAIN注解參數(shù),結果如下,可以用在子線程處理耗時操作,然后返回值需要切回到主線程刷新UI的場景:

代碼測試2:
#EventBusActivity
msg1Btn.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View view) {
//發(fā)送數(shù)據給監(jiān)聽者
Log.i(TAG, "post thread: " + Thread.currentThread().getName());
EventBus.getDefault().post("msg1 - coming!??!");
Log.i(TAG, "post thread: " + Thread.currentThread().getName());
EventBus.getDefault().post("msg1-1 - coming?。?!");
}
});
#EventBusService
@Subscribe(threadMode = ThreadMode.MAIN)
public void onMsgEventReceived(String msg) {
Log.i(TAG, "onMsgEventReceived thread: " + Thread.currentThread().getName());
Log.i(TAG, "String msg: " + msg);
try {
Thread.sleep(2 * 1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
發(fā)送post放在主線程并連續(xù)發(fā)送兩次,接收事件的函數(shù)加上耗時操作,運行結果如下,兩次post打印就相隔2s,第二次post需要等第一次事件接收處理完以后才能發(fā)出,所以主線程會阻塞:

同樣修改下發(fā)出post的代碼放到子線程后沒有這個問題,結果如下:

-
ThreadMode.MAIN_ORDERED:無論在那個線程發(fā)送事件,都先將事件入隊列,然后通過
Handler切換到主線程,依次處理事件。
代碼測試:
#EventBusActivity
msg1Btn.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View view) {
//發(fā)送數(shù)據給監(jiān)聽者
Log.i(TAG, "post thread: " + Thread.currentThread().getName());
EventBus.getDefault().post("msg1 - coming?。。?);
Log.i(TAG, "post thread: " + Thread.currentThread().getName());
EventBus.getDefault().post("msg1-1 - coming?。?!");
}
});
#EventBusService
@Subscribe(threadMode = ThreadMode.MAIN_ORDERED)
public void onMsgEventReceived(String msg) {
Log.i(TAG, "onMsgEventReceived thread: " + Thread.currentThread().getName());
Log.i(TAG, "String msg: " + msg);
try {
Thread.sleep(2 * 1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
代碼和ThreadMode.MAIN測試2一樣,只是將threadMode改為了MAIN_ORDERED,運行結果如下,兩次post可以連續(xù)發(fā)出:

-
ThreadMode.BACKGROUND:如果在主線程發(fā)送事件,則先將事件入隊列,然后通過線程池依次處理事件;如果在子線程發(fā)送事件,則直接在發(fā)送事件的子線程處理事件。
代碼測試1:
msg1Btn.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View view) {
//發(fā)送數(shù)據給監(jiān)聽者
Log.i(TAG, "post thread: " + Thread.currentThread().getName());
EventBus.getDefault().post("msg1 - coming?。?!");
}
});
#EventBusService
@Subscribe(threadMode = ThreadMode.BACKGROUND)
public void onMsgEventReceived(String msg) {
Log.i(TAG, "onMsgEventReceived thread: " + Thread.currentThread().getName());
Log.i(TAG, "String msg: " + msg);
}
運行結果如下,主線程發(fā)送事件,線程池依次處理事件:

代碼測試2:
msg1Btn.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View view) {
//發(fā)送數(shù)據給監(jiān)聽者
new Thread(new Runnable() {
@Override
public void run() {
Log.i(TAG, "post thread: " + Thread.currentThread().getName());
EventBus.getDefault().post("msg1 - coming?。?!");
}
}).start();
}
});
#EventBusService
@Subscribe(threadMode = ThreadMode.BACKGROUND)
public void onMsgEventReceived(String msg) {
Log.i(TAG, "onMsgEventReceived thread: " + Thread.currentThread().getName());
Log.i(TAG, "String msg: " + msg);
}
運行結果,子線程發(fā)送事件,則直接在發(fā)送事件的子線程處理事件:

-
ThreadMode.ASYNC:無論在那個線程發(fā)送事件,都將事件入隊列,然后通過線程池處理。
代碼測試1:
msg1Btn.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View view) {
//發(fā)送數(shù)據給監(jiān)聽者
Log.i(TAG, "post thread: " + Thread.currentThread().getName());
EventBus.getDefault().post("msg1 - coming?。?!");
}
});
#EventBusService
@Subscribe(threadMode = ThreadMode.ASYNC)
public void onMsgEventReceived(String msg) {
Log.i(TAG, "onMsgEventReceived thread: " + Thread.currentThread().getName());
Log.i(TAG, "String msg: " + msg);
}
運行結果,主線程發(fā)送,線程池處理:

代碼測試2:
msg1Btn.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View view) {
//發(fā)送數(shù)據給監(jiān)聽者
new Thread(new Runnable() {
@Override
public void run() {
Log.i(TAG, "post thread: " + Thread.currentThread().getName());
EventBus.getDefault().post("msg1 - coming?。?!");
}
}).start();
}
});
#EventBusService
@Subscribe(threadMode = ThreadMode.ASYNC)
public void onMsgEventReceived(String msg) {
Log.i(TAG, "onMsgEventReceived thread: " + Thread.currentThread().getName());
Log.i(TAG, "String msg: " + msg);
}
運行結果,子線程發(fā)送,線程池處理:

2、sticky:
sticky是否為粘性監(jiān)聽,boolean類型,默認值為false。正常我們都是先訂閱,才能接收到發(fā)出的事件,sticky的作用就是訂閱者可以先不進行注冊,事件先發(fā)出,再注冊訂閱者,同樣可以接收到事件,并進行處理。
3、priority:
priority是優(yōu)先級,int類型,默認值為0。值越大,優(yōu)先級越高,越優(yōu)先接收到事件。值得注意的是,只有在post事件和事件接收處理,處于同一個線程環(huán)境的時候,才有意義。
參考文章
EventBus詳解 (詳解 + 原理)