EventBus(事件總線)
歷史背景
它是由開源組織greenrobot貢獻的Android事件發(fā)布/訂閱的框架.
主要功能
通過解耦發(fā)布者和訂閱者來簡化Android中事件的傳遞(通俗來講,就是一個人發(fā)事件的消息告訴另一個人該做什么,或者是某個人想將數(shù)據(jù)交給另一個人),這個事件傳遞既可用于四大組件之間的數(shù)據(jù)傳遞(也叫通信),也可以用于異步線程和主線程之間的通信.代替了傳統(tǒng)的事件傳遞方式:Handler , BrocastReceiver , 接口回調(diào),在Fragment , Activity , Service , 線程之間傳遞數(shù)據(jù),執(zhí)行方法.一句話:任何一個java類通可以用這個通訊
概念
事件(Event)
又可稱為消息.其實就是一個對象.該對象可以是任意數(shù)據(jù)類型,不像Intent,Bundle傳遞數(shù)據(jù)一樣局限.通常這個對象都是通過自定義一個事件類來實例化的
訂閱者(Subscriber)
訂閱某種事件類型的對象,也就是訂閱接收事件信息的對象.當有發(fā)布者發(fā)布這類事件后,EventBus會執(zhí)行訂閱者的訂閱函數(shù),這個訂閱函數(shù)俗稱事件響應(yīng)函數(shù).訂閱者通過注冊(register)接口訂閱某個事件類型,unregister進行接口退訂.注意:訂閱者存在優(yōu)先級,優(yōu)先級高的訂閱者可以取消事件繼續(xù)向優(yōu)先級低的訂閱者分發(fā),默認所有訂閱者優(yōu)先級都為0.
發(fā)布者(Publisher)
發(fā)布某種事件的對象,通過post接口發(fā)布事件.
簡單的使用案例
分別創(chuàng)建兩個Activity,分別是SubscriberActivity(作為訂閱者),PublisherActivity(作為發(fā)布者),PublisherActivity發(fā)布事件類型給SubscriberActivity接收,也就是發(fā)布者發(fā)布事件類型給訂閱者接收,訂閱者在訂閱方法中做相應(yīng)的處理.
創(chuàng)建事件類型,通常一個自定義類,這里我們命名為MyEvent
package jansonwang.example.com.myapplication;
/**
* Created by Jason Wang on 2017/5/22.
* 這是一個事件類型,它可以攜帶各種各樣的數(shù)據(jù)類型
*/
public class MyEvent {
public String msg;
public MyEvent(String msg){
this.msg = msg;
}
}
創(chuàng)建SubscriberActivity(作為訂閱者),并且注冊訂閱接口,自定義訂閱函數(shù),反注冊訂閱接口
package jansonwang.example.com.myapplication;
import android.content.Intent;
import android.os.Bundle;
import android.support.v7.app.AppCompatActivity;
import android.util.Log;
import android.view.View;
import android.widget.Toast;
import org.greenrobot.eventbus.EventBus;
import org.greenrobot.eventbus.Subscribe;
import org.greenrobot.eventbus.ThreadMode;
public class SubscriberActivity extends AppCompatActivity {
private static final String TAG ="SubscriberActivity";
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
//EventBus.getDefault();創(chuàng)建單個事件總線對象
EventBus.getDefault().register(this); //注冊事件總線
}
public void start(View view) {
Intent intent = new Intent(this, PublisherActivity.class);
startActivity(intent);
}
/**
* 接收發(fā)布者的事件消息,
* ThreadMode.MAIN模式:無論發(fā)布者在子線程中還是主線程中發(fā)送事件信息,最后訂閱者方法都會在主線程中執(zhí)行,所以這種模式下不能在訂閱者方法中進行耗時操作
*/
@Subscribe(threadMode = ThreadMode.MAIN)
public void receiveEventOnMain(MyEvent event) {
Log.d(TAG, "receiveEventOnMain: " + event.msg + " " + Thread.currentThread().getName());
Toast.makeText(SubscriberActivity.this, "ThreadMode.MAIN", Toast.LENGTH_SHORT).show();
}
/**
* 接收發(fā)布者的事件消息
* ThreadMode.POSTING模式:發(fā)布者在哪個線程中發(fā)布事件消息,訂閱者方法就會在哪個線程中執(zhí)行,所以這個模式下也不能進行耗時操作,會延遲分發(fā)
* @param event
*/
@Subscribe(threadMode = ThreadMode.POSTING)
public void receiveEventPosting(MyEvent event){
Log.d(TAG, "receiveEventPosting: " + event.msg + " " + Thread.currentThread().getName());
runOnUiThread(new Runnable() {
@Override
public void run() {
Toast.makeText(SubscriberActivity.this, "ThreadMode.POSTING", Toast.LENGTH_SHORT).show();
}
});
}
/**
* 接收發(fā)布者的事件消息
* ThreadMode.ASYNC模式:發(fā)布者無論在哪個線程中發(fā)布事件消息,訂閱者方法都會自己重新創(chuàng)建一個子線程執(zhí)行
* @param event
*/
@Subscribe(threadMode = ThreadMode.ASYNC)
public void receiveEventASYNC(MyEvent event){
Log.d(TAG, "receiveEventPosting: " + event.msg + " " + Thread.currentThread().getName());
runOnUiThread(new Runnable() {
@Override
public void run() {
Toast.makeText(SubscriberActivity.this, "ThreadMode.ASYNC", Toast.LENGTH_SHORT).show();
}
});
}
/**
* 接收發(fā)布者的事件消息
* ThreadMode.BACKGROUND模式:發(fā)布者如果在子線程中發(fā)布事件消息,訂閱者方法就會在同一個子線程中執(zhí)行;
* 發(fā)布者如果在主線程中發(fā)布事件消息,訂閱者方法就會自己創(chuàng)建一個子線程執(zhí)行
* @param event
*/
@Subscribe(threadMode = ThreadMode.BACKGROUND)
public void receiveEventBACKGROUND(MyEvent event){
Log.d(TAG, "receiveEventPosting: " + event.msg + " " + Thread.currentThread().getName());
runOnUiThread(new Runnable() {
@Override
public void run() {
Toast.makeText(SubscriberActivity.this, "ThreadMode.BACKGROUND", Toast.LENGTH_SHORT).show();
}
});
}
@Override
protected void onDestroy() {
super.onDestroy();
//反注冊事件總線
EventBus.getDefault().unregister(this);
}
}
創(chuàng)建PublisherActivity(作為發(fā)布者),并且發(fā)布事件
package jansonwang.example.com.myapplication;
import android.os.Bundle;
import android.support.annotation.Nullable;
import android.support.v7.app.AppCompatActivity;
import android.view.View;
import org.greenrobot.eventbus.EventBus;
/**
* Created by Jason Wang on 2017/5/22.
*/
public class PublisherActivity extends AppCompatActivity {
@Override
protected void onCreate(@Nullable Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_publisher);
}
/**
* 在主線程中發(fā)布消息
* @param view
*/
public void send1(View view) {
EventBus.getDefault().post(new MyEvent("發(fā)布者向訂閱者發(fā)送事件消息"));
}
/**
* 在子線程中發(fā)布消息
* @param view
*/
public void send2(View view) {
new Thread(new Runnable() {
@Override
public void run() {
EventBus.getDefault().post(new MyEvent("發(fā)布者向訂閱者發(fā)送事件消息"));
}
}).start();
}
}
*主要步驟:
1.訂閱者注冊訂閱接口
//EventBus.getDefault();創(chuàng)建單個事件總線對象
EventBus.getDefault().register(訂閱者類); //注冊事件總線
2.在訂閱者類中自定義訂閱函數(shù)(記得在函數(shù)頭上加引用,例如 @Subscribe(threadMode = ThreadMode.MAIN)),用來接收發(fā)布者發(fā)布事件類型,
/**
* 接收發(fā)布者的事件消息,
* ThreadMode.MAIN模式:無論發(fā)布者在子線程中還是主線程中發(fā)送事件信息,最后訂閱者方法都會在主線程中執(zhí)行,所以這種模式下不能在訂閱者方法中進行耗時操作
*/
@Subscribe(threadMode = ThreadMode.MAIN)
public void receiveEventOnMain(MyEvent event) {
Log.d(TAG, "receiveEventOnMain: " + event.msg + " " + Thread.currentThread().getName());
Toast.makeText(SubscriberActivity.this, "ThreadMode.MAIN", Toast.LENGTH_SHORT).show();
}
/**
* 接收發(fā)布者的事件消息
* ThreadMode.POSTING模式:發(fā)布者在哪個線程中發(fā)布事件消息,訂閱者方法就會在哪個線程中執(zhí)行,所以這個模式下也不能進行耗時操作,會延遲分發(fā)
* @param event
*/
@Subscribe(threadMode = ThreadMode.POSTING)
public void receiveEventPosting(MyEvent event){
Log.d(TAG, "receiveEventPosting: " + event.msg + " " + Thread.currentThread().getName());
runOnUiThread(new Runnable() {
@Override
public void run() {
Toast.makeText(SubscriberActivity.this, "ThreadMode.POSTING", Toast.LENGTH_SHORT).show();
}
});
}
/**
* 接收發(fā)布者的事件消息
* ThreadMode.ASYNC模式:發(fā)布者無論在哪個線程中發(fā)布事件消息,訂閱者方法都會自己重新創(chuàng)建一個子線程執(zhí)行
* @param event
*/
@Subscribe(threadMode = ThreadMode.ASYNC)
public void receiveEventASYNC(MyEvent event){
Log.d(TAG, "receiveEventPosting: " + event.msg + " " + Thread.currentThread().getName());
runOnUiThread(new Runnable() {
@Override
public void run() {
Toast.makeText(SubscriberActivity.this, "ThreadMode.ASYNC", Toast.LENGTH_SHORT).show();
}
});
}
/**
* 接收發(fā)布者的事件消息
* ThreadMode.BACKGROUND模式:發(fā)布者如果在子線程中發(fā)布事件消息,訂閱者方法就會在同一個子線程中執(zhí)行;
* 發(fā)布者如果在主線程中發(fā)布事件消息,訂閱者方法就會自己創(chuàng)建一個子線程執(zhí)行
* @param event
*/
@Subscribe(threadMode = ThreadMode.BACKGROUND)
public void receiveEventBACKGROUND(MyEvent event){
Log.d(TAG, "receiveEventPosting: " + event.msg + " " + Thread.currentThread().getName());
runOnUiThread(new Runnable() {
@Override
public void run() {
Toast.makeText(SubscriberActivity.this, "ThreadMode.BACKGROUND", Toast.LENGTH_SHORT).show();
}
});
}
3.創(chuàng)建一個類,作為要發(fā)布的事件類型
package jansonwang.example.com.myapplication;
/**
* Created by Jason Wang on 2017/5/22.
* 這是一個事件類型,它可以攜帶各種各樣的數(shù)據(jù)類型
*/
public class 事件類型 {
public String msg;
public MyEvent(String msg){
this.msg = msg;
}
}
4.發(fā)布者發(fā)布事件類型
EventBus.getDefault().post(事件類型對象);
每個步驟的源碼分析
EventBus.getDefault();//創(chuàng)建事件總線對象
這段代碼采用的是單例模式:
```
//兩個非空旺遮,一個加鎖
public static EventBus getDefault() {
if (defaultInstance == null) {
synchronized (EventBus.class) {
if (defaultInstance == null) {
defaultInstance = new EventBus();
}
}
}
return defaultInstance;
}
```
下面是優(yōu)化歷史
//簡單單例陨界,當多線程時顿痪,還是會創(chuàng)建多個示例
public static EventBus getDefault() {
if (defaultInstance == null) {
defaultInstance = new EventBus();
}
return defaultInstance;
}
//加鎖單例镊辕,每次調(diào)用都檢查是否加鎖
public static synchronized EventBus getDefault() {
if (defaultInstance == null) {
defaultInstance = new EventBus();
}
return defaultInstance;
}
//兩個非空,一個加鎖
public static EventBus getDefault() {
if (defaultInstance == null) {
synchronized (EventBus.class) {
if (defaultInstance == null) {
defaultInstance = new EventBus();
}
}
}
return defaultInstance;
}
EventBus.getDefault().register(this); //注冊事件總線
下面是注冊的源碼
public void register(Object subscriber) {
Class<?> subscriberClass = subscriber.getClass();
//找到訂閱者中所有的訂閱方法
List<SubscriberMethod> subscriberMethods = subscriberMethodFinder.findSubscriberMethods(subscriberClass);
synchronized (this) {
for (SubscriberMethod subscriberMethod : subscriberMethods) {
//將訂閱方法記錄下來保存到對應(yīng)事件的訂閱列表中
//Map<Class<?>, CopyOnWriteArrayList<Subscription>> subscriptionsByEventType
subscribe(subscriber, subscriberMethod);
}
}
}
EventBus.getDefault().post(事件類型對象)
下面是發(fā)布者發(fā)布事件的源碼:
public void post(Object event) {
PostingThreadState postingState = currentPostingThreadState.get();
List<Object> eventQueue = postingState.eventQueue;
eventQueue.add(event);.//添加到事件隊列
if (!postingState.isPosting) {
postingState.isMainThread = Looper.getMainLooper() == Looper.myLooper();
postingState.isPosting = true;
if (postingState.canceled) {
throw new EventBusException("Internal error. Abort state was not reset");
}
try {
while (!eventQueue.isEmpty()) {
//發(fā)布的單個事件
postSingleEvent(eventQueue.remove(0), postingState);
}
} finally {
postingState.isPosting = false;
postingState.isMainThread = false;
}
}
}
private void postSingleEvent(Object event, PostingThreadState postingState) throws Error {
Class<?> eventClass = event.getClass();
boolean subscriptionFound = false;
if (eventInheritance) {
........
} else {
//發(fā)布對應(yīng)事件類型的事件
subscriptionFound = postSingleEventForEventType(event, postingState, eventClass);
}
.........
}
private boolean postSingleEventForEventType(Object event, PostingThreadState postingState, Class<?> eventClass) {
CopyOnWriteArrayList<Subscription> subscriptions;
synchronized (this) {
subscriptions = subscriptionsByEventType.get(eventClass);//獲取對應(yīng)事件的訂閱列表
}
if (subscriptions != null && !subscriptions.isEmpty()) {
//遍歷訂閱列表
for (Subscription subscription : subscriptions) {
postingState.event = event;
postingState.subscription = subscription;
boolean aborted = false;
try {
//發(fā)布事件到一個訂閱
postToSubscription(subscription, event, postingState.isMainThread);
aborted = postingState.canceled;
}
........
}
return true;
}
......
}
private void postToSubscription(Subscription subscription, Object event, boolean isMainThread) {
//判斷訂閱方法的線程模型
switch (subscription.subscriberMethod.threadMode) {
case POSTING:
//POSTING線程模型下蚁袭,直接反射調(diào)用訂閱方法
invokeSubscriber(subscription, event);
break;
case MAIN:
//MAIN線程模型
if (isMainThread) {
//如果當前是主線程丑蛤,則直接反射調(diào)用訂閱方法
invokeSubscriber(subscription, event);
} else {
//如果當前不是主線程,則使用綁定主線程Looper的Handler在主線程調(diào)用訂閱方法
mainThreadPoster.enqueue(subscription, event);
}
break;
case BACKGROUND:
//BACKGROUND線程模型
if (isMainThread) {
//如果當前是主線程撕阎,則在EventBus內(nèi)部的線程池中執(zhí)行
backgroundPoster.enqueue(subscription, event);
} else {
//如果當前是子線程,則直接在該子線程反射調(diào)用訂閱方法
invokeSubscriber(subscription, event);
}
break;
case ASYNC:
//ASYNC線程模型
//直接在EventBus的線程池中執(zhí)行
asyncPoster.enqueue(subscription, event);
break;
default:
throw new IllegalStateException("Unknown thread mode: " + subscription.subscriberMethod.threadMode);
}
}