目录
1.8.1.配置索引:在build.gradle中添加如下配置
2.2.2.通过Class subscriberClass解析出订阅者声明的所有事件处理方法、方法参数类型
2.2.3.subscribe(subscriber, subscriberMethod)方法的实现
2.3.4.postSingleEventForEventType
一 EventBus 3.1.1
1.1.简介
简介:
EventBus是一个基于观察者模式的事件发布 / 订阅的消息总线框架。目的用于简化(Android)组件、线程间通讯,可轻易切换、开辟线程,简洁,方便,体积小,效率高 ……
EventBus3.0跟先前版本的区别在于加入了annotation @Subscribe,取代了以前约定命名的方式。
原理:
EventBus中,当被观察者(事件对象)产生或变更的时候,会通过EventBus把 事件对象“通知给”观察者(订阅者对象)(触发事件回调方法)。整个过程中EventBus充当了中介的角色,将原本 被观察者(事件对象)所需要承担的责任抽离出来由自己承担,缓存并管理 观察者 列表,并与 被观察者(事件对象)建立对应关系,并控制时机,将被 观察者(事件对象)“通知给” 观察者(订阅者对象)。
EventBus中 被观察者 和 观察者 是通过 声明的 事件对象类型(可以理解为一种协议) 进行匹配的,即EventBus将特定的 事件对象 发送给能接收该 事件类型 的 订阅者对象(订阅者在注册EventBus的时候,就已经告诉EventBus,自己需要哪类事件了)。
优:
代码够简洁,消息发布者和订阅者之间的耦合度够低!并且可以动态设置事件处理线程以及优先级。快速,轻量!
劣:
每个事件都要创建一个事件类,加大维护成本(可做封装优化,后面会介绍方案)。
说明:
由于EventBus使用了反射,关于性能问题 ……
EventBus不仅可以使用注解处理器预处理获取订阅信息,也会将订阅者的方法缓存到METHOD_CACHE里避免重复查找,只有在最后invoke()方法的时候会比直接调用多出一些性能损耗,其实可以忽略不计。
结构图:
SDK:
EventBus :http://greenrobot.org/eventbus/
GitHub:https://github.com/greenrobot/EventBus
version:
http://search.maven.org/#search%7Cga%7C1%7Cg%3A%22org.greenrobot%22%20AND%20a%3A%22eventbus%22
https://mvnrepository.com/
注意:
com.google.common.eventbus. EventBus
org.greenrobot.eventbus.EventBus
两个EventBus不一样
1.2.实现
(1)订阅者类中需要注册和解绑EventBus
(2)声明事件类型
(3)在事件产生或者变更的类中直接post事件对象。
1.2.1.配置(EventBusBuilder)
EventBusBuilder提供了EventBus的配置接口,指定了EventBus的一些行为,用于输出log,查错,调试等。比如配置日志输出模式(开发版本/发布版本),配置错误处理模式(开发版本遇错误崩溃 / 发布版本遇错误崩溃)等等。
// 修改默认实现的配置:debug模式下要抛出异常
EventBus.builder().throwSubscriberException(BuildConfig.DEBUG)
.installDefaultEventBus();
必须在第一次EventBus.getDefault()之前配置,且只能设置一次。建议在application.onCreate()调用。
installDefaultEventBus这个方法用来将一个自定义的EventBusBuider对象配置到EventBus中去,并替换掉defaultInstance。
注意:这里需要在Default EventBus 对象还没生成的时候执行这段代码。如果已经有默认的EventBus对象存在了再installDefaultEventBus就会抛异常。所以最好在app启动的时候加入代码!
1.2.2.声明事件类型
public class MessageEvent {
public final String message;
public MessageEvent(String message) {
this.message = message;
}
}
1.2.3.在订阅者类中定义消息处理方法(事件回调方法)
@Subscribe
public void onMessageEvent(MessageEvent event) {
Toast.makeText(getActivity(), event.message, Toast.LENGTH_SHORT).show();
}
// This method will be called when a SomeOtherEvent is posted
@Subscribe
public void handleSomethingElse(SomeOtherEvent event) {
doSomethingWith(event);
}
@Subscribe(threadMode = ThreadMode.POSTING)
public void onMessageEvent(MessageEvent event) {
doSomethingWith(event);
}
事件处理函数的访问权限必须为public!!!
在3.0之前,事件处理的方法也只能限定于onEvent、onEventMainThread、onEventBackgroundThread和onEventAsync,分别代表四种线程模型;在3.0之后,消息处理的方法可以随便取名,只需要添加一个注解@Subscribe,并且要指定线程模型(默认为POSTING)
1.2.4.在订阅者类中注册解绑EventBus
@Override
public void onStart() {
super.onStart();
EventBus.getDefault().register(this);
}
@Override
public void onStop() {
EventBus.getDefault().unregister(this);
super.onStop();
}
1.2.5.被订阅者类发送事件对象
EventBus.getDefault().post(new MessageEvent("Hello everyone!"));
1.3.线程模型(ThreadMode)
1.3.1.ThreadMode.POSTING
说明:
事件发布线程中执行(默认线程模型):事件在哪个线程发布(post),事件接收方法就在哪个线程中执行,即 发布事件 和 接收处理事件 在同一个线程。当该线程为主线程时,响应方法中不能有耗时操作,否则有卡主线程的风险,甚至有可能会引起ANR。
场景:
对于是否在主线程执行无要求,但若 Post 线程为主线程,不能处理耗时操作
// Called in the same thread (default)
@Subscribe(threadMode = ThreadMode.POSTING)
public void onMessageEvent(MessageEvent messageEvent) {
doSomethingWith(event);
}
1.3.2.ThreadMode.MAIN
说明:
主线程(UI线程)中执行:如果事件发布线程就是主线程,则直接调用订阅者的事件回调方法,否则将通过主线程的 Handler 发送消息,通知订阅者在主线程中处理事件回调。
场景:
该线程模式可以用来更新UI,但是 不能处理耗时操作
// Called in Android UI's main thread
@Subscribe(threadMode = ThreadMode.MAIN)
public void onMessageEvent(MessageEvent messageEvent) {
doSomethingWith(event);
}
1.3.3.ThreadMode.BACKGROUND
说明:
后台线程中执行:如果事件发布线程不是主线程,则直接在该线程执行;否则,切换到后台单例线程执行事件回调方法,且多个方法公用同个后台线程,按顺序执行。
场景:
禁止进行UI更新操作,或禁止在主线程(UI线程)中执行,同样 避免处理耗时操作!
// Called in the background thread
@Subscribe(threadMode = ThreadMode. BACKGROUND)
public void onMessageEvent(MessageEvent messageEvent) {
doSomethingWith(event);
}
1.3.4.ThreadMode.ASYNC
说明:
异步线程执行:开辟新独立线程,EventBus内部使用了线程池,但是要尽量避免大量长时间运行的异步线程,限制并发线程数量可以通过EventBusBuilder修改,默认使用Executors.newCachedThreadPool()。
场景:
用来执行耗时操作,例如网络访问。
// Called in a separate thread
@Subscribe(threadMode = ThreadMode. ASYNC)
public void onMessageEvent(MessageEvent messageEvent) {
doSomethingWith(event);
}
1.4.粘性事件(StickyEvent)
1.4.1.场景分析:
1.背景描述:
正常事件发布处理流程应该是订阅者先在EventBus中注册,并告诉EventBus自己想订阅哪些类型的事件。之后当事件发布时,才有可能通过EventBus找到匹配的订阅者。然而,会有一种场景是事件先发布,订阅者之后才注册,这种情况下,事件发布时是找不到订阅者的 …… StickyEvent就是为了解决上述问题而生的。
2.具体应用场景:
(1)缓存:EventBus会将StickyEvent特定类型的最后一个实体保留在内存中,并可供明确查询。
(2)替代Intent传递数据给新的Activity
1.4.2.StickyEvent:
StickyEvent类似广播分类中的粘性广播,同类Event会一直在内存中保存最新的实体,覆盖原有实体。当检查到有可匹配的订阅者在EventBus注册的时候,EventBus会找到订阅者并将最新的实体“通知给”订阅者。如果没有可匹配的订阅者注册,Event消息实体会一直保留在内存中。
如此一来,订阅在发布事件之后,同样可以收到事件,且EventBus会在订阅者订阅之后立即找到它并回调其粘性事件处理方法。订阅/解除订阅和普通事件一样,但是声明事件回调方法稍有不同,需要注解中添加sticky = true。
在订阅在发布事件之后的场景中,必须使用postSticky方法发布事件,且必须使用注解中添加sticky = true的事件处理方法接收sticky事件。
实现原理:
postSticky中用了一个stickyEvents (ConcurrentHashMap< event.getClass(), event>)缓存了最新的事件实体。
public void postSticky(Object event) {
synchronized (stickyEvents) {
stickyEvents.put(event.getClass(), event);
}
// Should be posted after it is putted, in case the subscriber wants to remove immediately
post(event);
}
1.4.3.实现:
声明事件处理方法:
@Subscribe(threadMode = ThreadMode.MAIN,sticky = true) // 在ui线程执行
public void stickyEventMethod(MessageEvent event) {
// ......
}
事件发送:
EventBus.getDefault().postSticky(new MessageEvent ());
由于StickyEvent属于常驻事件,会常驻内存,所以有时候需要手动移除!
MessageEvent stickyEvent = EventBus.getDefault().getStickyEvent(MessageEvent.class);
// Better check that an event was actually posted before
if(stickyEvent != null) {
// "Consume" the sticky event
EventBus.getDefault().removeStickyEvent(stickyEvent);
//or
EventBus.getDefault().removeAllStickyEvents();
// Now do something with it
}
1.4.4.应用案例:
针对1.5.1 具体应用场景(2):替代Intent实现Activity间数据传递
(1)声明一个新的Activity B,并定义好StickyEvent事件处理方法,在onStart、onStop方法中注册 / 解注 EventBus。
public class ActivityB extends AppCompatActivity {
@Override
protected void onStart() {
super.onStart();
EventBus.getDefault().register(this);
}
@Subscribe(sticky = true)
public void dealSticky(StickyMessageEvent event) {
Log.i("DemoActivity", "dealSticky method" + event);
}
@Override
protected void onStop() {
super.onStop();
EventBus.getDefault().unregister(this);
}
}
(2)在原Activity A中启动新Activity B之前,发布包含数据信息的StickyMessageEvent,之后启动Activity B。
EventBus.getDefault().postSticky(new StickyMessageEvent());
Intent intent = new Intent();
intent.setClass(this, DemoActivity.class);
startActivity(intent);
(3)在Activity B中正常接收到StickyMessageEvent,解析其中数据信息进行处理 ……
1.5.无订阅者事件(NoSubscriberEvent)
没有订阅者的消息默认会被重新包装为NoSubscriberEvent,并打印Logcat:
No subscribers registered for event class org.greenrobot.eventbus.NoSubscriberEvent
可以做一些全局捕获处理,也可以通过EventBusBuilder设置是否默认发送NoSubscriberEvent,默认是打开的。(当设置为false,上述logcat就不会打印了)
EventBus.builder().sendNoSubscriberEvent(false).installDefaultEventBus();
1.6.事件优先级(priority)
priority越大,级别越高,越优先获得消息
@Subscribe(threadMode = ThreadMode.MAIN,priority = 100)
1.7.终止事件传递
发送有序广播可以终止广播的继续往下传递,EventBus也实现了此功能
// 优先级高的订阅者可以终止事件往下传
EventBus.getDe