EventBus 是Android 发布/订阅事件总线,可简化 Activities、Fragments、Threads、Services 等组件间的消息传递。EventBus使用了发布者/订阅者模式,其原理图如下:
优势:
在module的build.gredle 文件中的dependencies标签中添加
implementation 'org.greenrobot:eventbus:3.X.X'
PS:目前最新版本是3.1.1
Event:事件,它可以是任意类型。
Subscriber:事件订阅者。在EventBus3.0之前我们必须定义以onEvent开头的那几个方法,分别是onEvent、onEventMainThread、onEventBackgroundThread和onEventAsync,而在3.0之后事件处理的方法名可以随意取,不过需要加上注解@subscribe(),并且指定线程模型,默认是POSTING。
Publisher:事件的发布者。我们可以在任意线程里发布事件,一般情况下,使用EventBus.getDefault()就可以得到一个EventBus对象,然后再调用post(Object)方法即可。
1.注册事件
EventBus.getDefault().register(this);
2.取消注册
EventBus.getDefault().unregister(this);
3.发送事件
EventBus.getDefault().post( “我发射了”);
EventBus.getDefault().postSticky(new updateTextEvent(“这是一个测试”)); //粘性事件: poststicky 事件:指在事件发布之后才注册的事件消费者也能接收到该事件的特殊类型,如切换新页面时,新页面的注册比旧页面的发送事件晚。EventBus3.0版本新增方法。
4.订阅处理事件:
处理消息的方法名字可以随便取。但是需要加一个注解@Subscribe,并且要指定线程模型。
/**
* 自定义一个方法 hello() ,用来接收事件。
* 方法名字可以随便写
* @return
*/
@Subscribe(threadMode = ThreadMode.MAIN)
public void hello (String event){
/* Do something */
Toast.makeText(this, event, Toast.LENGTH_SHORT).show();
};
5.自定义事件用法
自定义事件:
public class MessageEvent{
private String message;
public MessageEvent(String message){
this.message=message;
}
public String getMessage() {
return message;
}
public void setMessage(String message) {
this.message = message;
}
}
注册和取消注册事件用法与普通事件相同;
发送事件:
MessageEvent messageEvent = new MessageEvent();
messageEvent.setMessage("自定义事件")
EventBus.getDefault().post(messageEvent);
处理事件:
@Subscribe(threadMode = ThreadMode.MAIN)
public void XXX(MessageEvent messageEvent) {
...
}
在接收事件消息的方法中,可以通过注解的方式设置线程模型,EventBus内置了4中线程模型,分别是ThreadMode.POSTING、ThreadMode.MAIN、ThreadMode.BACKGROUND、ThreadMode.ASYNC。如订阅事时,采用添加注解的方法@Subscribe(threadMode = ThreadMode.MAIN)对事件的处理进行区别。
默认的线程模式,如果使用事件处理函数指定了线程模型为POSTING,那么该事件在哪个线程发布出来的,事件处理函数就会在这个线程中运行,也就是说发布事件和接收事件在同一个线程。此外,在线程模型为POSTING的事件处理函数中尽量避免执行耗时操作,因为它会阻塞事件的传递,甚至有可能会引起ANR。
事件的处理会在UI线程中执行。此外也不能在处理事件中执行比较耗时的操作,否则也会引起ANR。
如果事件是在UI线程中发布出来的,那么该事件处理函数就会在新的线程中运行,如果事件本来就是子线程中发布出来的,那么该事件处理函数直接在发布事件的线程中执行。在此事件处理函数中禁止进行UI更新操作。
无论事件在哪个线程发布,该事件处理函数都会在新建的子线程中执行,同样,此事件处理函数中禁止进行UI更新操作,因为这牵涉到UI的更新只能在 main thread中更新。
EventBus 中生产者和消费者模式的实现主要是在 PendingPostQueue里面。
PendingPostQueue 的实现比较简单,主要是通过在 enqueue 和 poll 的时候进行 synchronized 同步来实现的。
synchronized void enqueue(PendingPost pendingPost) {
if (pendingPost == null) {
throw new NullPointerException("null cannot be enqueued");
}
// 将 Post 插入到队列尾部
if (tail != null) {
tail.next = pendingPost;
tail = pendingPost;
} else if (head == null) {
// 在最开始的时候,建立头部和尾部的索引
head = tail = pendingPost;
} else {
throw new IllegalStateException("Head present, but no tail");
}
notifyAll();
}
synchronized PendingPost poll() {
PendingPost pendingPost = head;
// 从头部获取
if (head != null) {
head = head.next;
if (head == null) {
tail = null;
}
}
return pendingPost;
}
// 这里需要注意的地方是 PendingPost, 这里维护了一个 pendingPostPool 的池子, 当PendingPost 不再需要的时候,就释放回池子里面去,避免了新建对象的开销。
static void releasePendingPost(PendingPost pendingPost) {
pendingPost.event = null;
pendingPost.subscription = null;
pendingPost.next = null;
synchronized (pendingPostPool) {
// Don't let the pool grow indefinitely
if (pendingPostPool.size() < 10000) {
pendingPostPool.add(pendingPost);
}
}
}
每个ThreadModel (除了PostThread) 都维护了一个 Poster,这个Post里面维持了一个“生产者消费者模式”,来消费和使用事件。
private void postToSubscription(Subscription subscription, Object event, boolean isMainThread) {
switch (subscription.subscriberMethod.threadMode) {
case PostThread:
invokeSubscriber(subscription, event);
break;
case MainThread:
// 主线程的poster
if (isMainThread) {
invokeSubscriber(subscription, event);
} else {
mainThreadPoster.enqueue(subscription, event);
}
break;
case BackgroundThread:
if (isMainThread) {
backgroundPoster.enqueue(subscription, event);
} else {
invokeSubscriber(subscription, event);
}
break;
case Async:
asyncPoster.enqueue(subscription, event);
break;
default:
throw new IllegalStateException("Unknown thread mode: " + subscription.subscriberMethod.threadMode);
}
}
EventBus的优势在于调度灵活。不依赖于 Context,使用时无需像广播一样关注 Context 的注入与传递,也解除了Handler所带来的耦合,父类对于通知的监听和处理可以继承给子类,这对于简化代码至关重要;通知的优先级,能够保证 Subscriber 关注最重要的通知;粘滞事件(sticky events)能够保证通知不会因 Subscriber 的不在场而忽略。可继承、优先级、粘滞,是 EventBus 比之于广播、观察者等方式最大的优点,它们使得创建结构良好组织紧密的通知系统成为可能。
缺点也很明显,EventBus中的事件分发是通过注解函数的参数类型决定的,这就导致了当接受者过多或相同参数时很难理清消息流。
广播是相对消耗时间、空间最多的一种方式。它是四大组件之一,许多系统级的事件都是通过广播来通知的,比如:电量的变化、网络的变化、短信的接收和发生状态等。
优点:与sdk连接紧密,当需要与Android交互时非常方便。而且可以实现跨进程通讯。必要时还能启动Activity
缺点:资源占用较多,且需要依赖Context
handler一般用于线程间通讯。handler的定义类和内部类是绑定的,这就造成了事件发布者和接受者之间的高耦合。使用handler最明显的优点是发生问题时,可以非常明确、快速的进行定位。