一、EventBus使用步骤:
1. gradle导入EventBus库
implementation 'org.greenrobot:eventbus:3.1.1'
2. 代码用使用EventBus
(1). 定义自己所需要的Event事件类,在里面定义你需要“沟通”的内容==>Handler中的一个Message消息.
//如果你想实现一个简单的注册登录界面,那么你需要传递的信息是:Name(昵称)、Password(密码)、Message(用户信息)
public class MessageEvent {
private String mMessage;
private String mName;
private String mPassword;
public MessageEvent() {
}
public void setMessage(String message) {
this.mMessage = message;
}
public String getMessage() {
return mMessage;
}
public void setName(String name) {
this.mName = name;
}
public String getName() {
return mName;
}
public void setPassword(String password) {
this.mPassword = password;
}
public String getPassword() {
return mPassword;
}
@Override
public String toString() {
return "Name = " + mName + ", password = " + mPassword + ", message = " + mMessage;
}
}
(2). 注册/注销EventBus对象,以便在该类中使用之.
//如果你想在两个Activity之间相互传递上面所定义的MessageEvent消息,则需要在你想要处理事件的Activity中完成EventBus的注册以及注销
//建议:在Activity的标准生命周期中注册和注销EventBus,因为两者必须成对出现。如onStart()--onStop(); onCreate()--onDestory(); onResume()--onPause()
Activity:
protected void onStart() {
super.onStart();
EventBus.getDefault().register(this); //注册EventBus
}
protected void onStop() {
super.onStop();
EventBus.getDefault().unregister(this); //注销EventBus
}
(3). 声明并注释好订阅方法.
//此方法即是你想要拿到消息之后进行处理的method
@Subscribe(threadMode = ThreadMode.MAIN)
public void handleMessageEvent(MessageEvent event) {
mName = event.getName();
mPassword = event.getPassword();
mMessage = event.getMessage();
Toast.makeText(MainActivity.this, "handleMessageEvent: receive Message, name = " + mName +
", password = " + mPassword + ", message = " + mMessage, Toast.LENGTH_LONG).show();
}
(4). 发送消息事件
MessageEvent messageEvent = new MessageEvent();
messageEvent.setMessage("Hello world");
messageEvent.setName("张三");
messageEvent.setPassword("123456"); //定义好需要发送消息的Event类里的内容
EventBus.getDefault().post(messageEvent); //发送该EventMessage
至此,EventBus已经成功在你的项目里面搭建好并完成消息发送+处理了。
二、EventBus的ThreadMode
在上面的第三个步骤,我们可以看到,在声明并注释订阅方法时,需要指定一个threadMode。这个threadMode,其实就是指定当前handleEvent的方法跑在哪一个线程里面。常用的ThreadMode主要有四个: POSTING、MAIN、BACKGROUND、ASYNC。
(1). ThreadMode.POSTING 当前handleEvent的方法的执行线程,和post这个Event的线程保持一致。如果是在主线程post该Event,handleEvent则在主线程;如果是在异步线程,则跑在同一个异步线程中。
//如果你在postEvent和handleEvent之间需要保持线程的同步,则需要使用此Mode
(2). ThreadMode.MAIN 当前handleEvent的方法的执行线程永远都在主线程,不管post这个Event的线程是在主线程还是异步线程。
//如果在handleEvent中需要处理一下UI的工作,则需要使用此Mode
(3). ThreadMode.BACKGROUND 当前handleEvent方法的执行线程永远在异步线程。如果在主线程post该Event,则new一个新的异步线程处理该方法;如果是在异步线程post,则直接在该异步线程处理。
//如果多个Subscribe注册了同一个MessageEvent,则会按顺序一个一个传下去处理
(4). ThreadMode.ASYNC 不管post该Event的线程是在主线程或者是异步线程,均会开启新的异步线程来执行handleEvent方法
//如果在handleEvent中需要处理耗时的工作,使用此Mode
如果想要进一步深入了解EventBus的ThreadMode,建议访问EventBus的主页阅读: http://greenrobot.org/eventbus/documentation/delivery-threads-threadmode/
三、EventBus源码深入了解
1.发送消息事件,调用到EventBus的post()方法,该方法主要是将当前的event塞到一个eventQueue队列中,然后再从队列中出列一个Event,并将其传到postSingleEvent()方法
//如果不了解队列的工作原理和特性,建议先去源码查看一下Queue的使用
/** Posts the given event to the event bus. */
public void post(Object event) {
PostingThreadState postingState = currentPostingThreadState.get();
List<Object> eventQueue = postingState.eventQueue;
eventQueue.add(event);
if (!postingState.isPosting) {
postingState.isMainThread = isMainThread();
postingState.isPosting = true;
if (postingState.canceled) {
throw new EventBusException("Internal error. Abort state was not reset");
}
try {
while (!eventQueue.isEmpty()) {
postSingleEvent(eventQueue.remove(0), postingState); //关键方法
}
} finally {
postingState.isPosting = false;
postingState.isMainThread = false;
}
}
}
2. 在前面我们可以看到,messageEvent是通过参数传递到了postSingleEvent()方法,而这个方法则是根据传进来的event类来分发相关的事件处理
private void postSingleEvent(Object event, PostingThreadState postingState) throws Error {
Class<?> eventClass = event.getClass();
boolean subscriptionFound = false;
if (eventInheritance) {
List<Class<?>> eventTypes = lookupAllEventTypes(eventClass);
int countTypes = eventTypes.size();
for (int h = 0; h < countTypes; h++) {
Class<?> clazz = eventTypes.get(h);
subscriptionFound |= postSingleEventForEventType(event, postingState, clazz); //根据拿到的event和evnet的类型来进行消息的分发处理
}
} else {
subscriptionFound = postSingleEventForEventType(event, postingState, eventClass); //根据拿到的event和evnet的类型来进行消息的分发处理
}
if (!subscriptionFound) {
if (logNoSubscriberMessages) {
logger.log(Level.FINE, "No subscribers registered for event " + eventClass);
}
if (sendNoSubscriberEvent && eventClass != NoSubscriberEvent.class &&
eventClass != SubscriberExceptionEvent.class) {
post(new NoSubscriberEvent(this, event));
}
}
}
3. 继续往postSingleEventForEventType()方法走下去,这个方法比较简单,就是根据事件的类型,从所有注册了订阅方法里面匹配出符合相关类型的subscribe,并将此事件通过postToSubscription()方法继续传下去
private boolean postSingleEventForEventType(Object event, PostingThreadState postingState, Class<?> eventClass) {
CopyOnWriteArrayList<Subscription> subscriptions;
synchronized (this) {
subscriptions = subscriptionsByEventType.get(eventClass);
}
if (subscriptions != null && !subscriptions.isEmpty()) {
for (Subscription subscription : subscriptions) {
postingState.event = event;
postingState.subscription = subscription;
boolean aborted = false;
try {
postToSubscription(subscription, event, postingState.isMainThread);
aborted = postingState.canceled;
} finally {
postingState.event = null;
postingState.subscription = null;
postingState.canceled = false;
}
if (aborted) {
break;
}
}
return true;
}
return false;
}
4. 终于来到了最关键的地方了,在这里我们可以看到,EventBus是根据当前注册的订阅方法的threadMode,分别将event通过不同的方式传到相关的订阅方法。
private void postToSubscription(Subscription subscription, Object event, boolean isMainThread) {
switch (subscription.subscriberMethod.threadMode) {
case POSTING: //posting, 则直接调用subscribe过的方法,从这里可以看到,系统没有新开任何线程去调用订阅方法,所以posting类型是直接走在调用EventBus.getDefault().post(messageEvent)的线程的
invokeSubscriber(subscription, event);
break;
case MAIN: //main, 判断当前是否为主线程,如果是主线程,则直接调用订阅方法,否则,将该event以及subscribe入列到mainThreadPoster,一个主线程的poster中。具体这个mainThreadPoster是如果工作的,下面会详细讲解
if (isMainThread) {
invokeSubscriber(subscription, event);
} else {
mainThreadPoster.enqueue(subscription, event);
}
break;
case MAIN_ORDERED: //main_ordered, 这个和main很相近,但是它是有一个顺序要求的。由于这个threadMode并不常用,所以在这里不做具体解释,有兴趣的同学可以在EventBus的官网上找到它的具体解释以及用法
if (mainThreadPoster != null) {
mainThreadPoster.enqueue(subscription, event);
} else {
// temporary: technically not correct as poster not decoupled from subscriber
invokeSubscriber(subscription, event);
}
break;
case BACKGROUND: //background, 如果当前是主线程,则将event入列到backgroundPoster中,利用background线程进行事件的分发。这里使用到了backgroundPoster,和main一下,会在后面界面这个poster的工作原理
if (isMainThread) {
backgroundPoster.enqueue(subscription, event);
} else {
invokeSubscriber(subscription, event);
}
break;
case ASYNC: //async, 这个和一开始讲的一样,不管是异步线程还是主线程,此event都是入列到asyncPoster中,通过asyncPoster来进行事件的分发
asyncPoster.enqueue(subscription, event);
break;
default:
throw new IllegalStateException("Unknown thread mode: " + subscription.subscriberMethod.threadMode);
}
}
5. 在前一步我们可以看到,ThreadMode为MAIN、Background、async,分别使用了mainThreadPoster、backgroundPoster以及asyncPoster。那么这三个poster究竟是什么呢?在这里给大家详细展示一下:
从命名来看,它们三者都有一个poster,所以我们可以很大胆的猜测,EventBus应该是定义了一个Poster接口或者是Poster的静态类,然后这三者都是继承与这个类或者实现了这个方法,然后分别完成相关的线程处理以及事件分发逻辑。
通过全局查看,我们发现了这个Poster接口,虽然找到了这个接口,但是这个接口具体是怎么实现/怎么使用的呢,我们继续往上找
interface Poster {
/**
* Enqueue an event to be posted for a particular subscription.
*
* @param subscription Subscription which will receive the event.
* @param event Event that will be posted to subscribers.
*/
void enqueue(Subscription subscription, Object event);
}
通过Eclipse的快捷键Ctrl+T,我们可以发现,一个有三个类实现了这个Poster接口:HandlerPoster、AsyncPoster以及BackgroundPoster。其实,从这里大家应该就能知道了,这三者其实对应的就是上面所说的mainThreadPoster、asyncPoster以及backgroundPoster。(如果不信的同学,可以继续往上找,看一下这三个类都是谁在用,然后在对应在EventBus类中postToSubscription这里的三个对象,就能知道了)
那我们在这里再仔细看一下,这三个Poster究竟是何方神圣:
(1). HandlerPoster
public class HandlerPoster extends Handler implements Poster {
private final PendingPostQueue queue;
private final int maxMillisInsideHandleMessage;
private final EventBus eventBus;
private boolean handlerActive;
protected HandlerPoster(EventBus eventBus, Looper looper, int maxMillisInsideHandleMessage) {
super(looper);
this.eventBus = eventBus;
this.maxMillisInsideHandleMessage = maxMillisInsideHandleMessage;
queue = new PendingPostQueue();
}
public void enqueue(Subscription subscription, Object event) {
PendingPost pendingPost = PendingPost.obtainPendingPost(subscription, event);
synchronized (this) {
queue.enqueue(pendingPost);
if (!handlerActive) {
handlerActive = true;
if (!sendMessage(obtainMessage())) {
throw new EventBusException("Could not send handler message");
}
}
}
}
@Override
public void handleMessage(Message msg) {
boolean rescheduled = false;
try {
long started = SystemClock.uptimeMillis();
while (true) {
PendingPost pendingPost = queue.poll();
if (pendingPost == null) {
synchronized (this) {
// Check again, this time in synchronized
pendingPost = queue.poll();
if (pendingPost == null) {
handlerActive = false;
return;
}
}
}
eventBus.invokeSubscriber(pendingPost);
long timeInMethod = SystemClock.uptimeMillis() - started;
if (timeInMethod >= maxMillisInsideHandleMessage) {
if (!sendMessage(obtainMessage())) {
throw new EventBusException("Could not send handler message");
}
rescheduled = true;
return;
}
}
} finally {
handlerActive = rescheduled;
}
}
}
看到这里,大家是不是都惊呆了!其实,HandlerPoster就是一个简单的集成Handler的类,通过sendMessage/handleMessage的方式完成了事件的处理分发。当然,大家可能会问,这个HandlerPoster是怎么变成主线程的mainThreadPoster的呢?
大家可以参考我上一篇文章 HandlerThread小讲解
(2). BackgroundPoster
final class BackgroundPoster implements Runnable, Poster {
private final PendingPostQueue queue;
private final EventBus eventBus;
private volatile boolean executorRunning;
BackgroundPoster(EventBus eventBus) {
this.eventBus = eventBus;
queue = new PendingPostQueue();
}
public void enqueue(Subscription subscription, Object event) {
PendingPost pendingPost = PendingPost.obtainPendingPost(subscription, event);
synchronized (this) {
queue.enqueue(pendingPost);
if (!executorRunning) {
executorRunning = true;
eventBus.getExecutorService().execute(this);
}
}
}
@Override
public void run() {
try {
try {
while (true) {
PendingPost pendingPost = queue.poll(1000);
if (pendingPost == null) {
synchronized (this) {
// Check again, this time in synchronized
pendingPost = queue.poll();
if (pendingPost == null) {
executorRunning = false;
return;
}
}
}
eventBus.invokeSubscriber(pendingPost);
}
} catch (InterruptedException e) {
eventBus.getLogger().log(Level.WARNING, Thread.currentThread().getName() + " was interruppted", e);
}
} finally {
executorRunning = false;
}
}
}
BackgroundPoster就更简单了,直接是实现了一个Runnable接口,直接在run()方法中完成事件的分发。Runnable接口是怎么工作的,大家具体可以参考 Google API,这里就不详细陈述了。
(3). AsyncPoster
class AsyncPoster implements Runnable, Poster {
private final PendingPostQueue queue;
private final EventBus eventBus;
AsyncPoster(EventBus eventBus) {
this.eventBus = eventBus;
queue = new PendingPostQueue();
}
public void enqueue(Subscription subscription, Object event) {
PendingPost pendingPost = PendingPost.obtainPendingPost(subscription, event);
queue.enqueue(pendingPost);
eventBus.getExecutorService().execute(this);
}
@Override
public void run() {
PendingPost pendingPost = queue.poll();
if(pendingPost == null) {
throw new IllegalStateException("No pending post available");
}
eventBus.invokeSubscriber(pendingPost);
}
}
AsyncPoster和BackgroundPoster的工作原理基本上是一致的
至此,整一个EventBust的工作流程以及原理基本上就介绍完成了,当然,这里主要是简单的带大家走了一遍EventBus从post到接收事件的逻辑,里面还有很多很值得深入理解学习的知识点,这就靠大家的挖掘了。