android EventBus
写在前面
1.前言
曾经,一层又一层的业务逻辑让我不知所措,一个又一个的回调让你头晕眼花,一个又一个的参数让你混乱不堪。EventBus,,一个耦合度低到令你害怕的框架。
2.什么是EventBus
EventBus是一个消息总线,以观察者模式实现,用于简化程序的组件、线程通信,可以轻易切换线程、开辟线程。EventBus3.0跟先前版本的区别在于加入了annotation @Subscribe,取代了以前约定命名的方式。
3.相似产品比较
| 产品名 | 开发者 | 备注 |
|---|---|---|
| EventBus | greenrobot | 用户最多,简洁,方便,小巧,文档简洁明了 |
| Guava | 一个庞大的工具类库,EventBus只是一个小功能 | |
| otto | square | fork guava ,用的人不少 |
| AndroidEventBus | 何红辉 | 模仿EventBus开发的 |
使用EventBus3.0三部曲
1.定义事件
public class MessageEvent { public final String message; public MessageEvent(String message) { this.message = message;
}
}2.准备订阅者
@Subscribe public void onMessageEvent(MessageEvent event){
Toast.makeText(getActivity(), event.message, Toast.LENGTH_SHORT).show();
}
@Subscribe public void handleSomethingElse(SomeOtherEvent event){
doSomethingWith(event);
} @Override
public void onStart() { super.onStart();
EventBus.getDefault().register(this);
} @Override
public void onStop() {
EventBus.getDefault().unregister(this); super.onStop();
}3.发送事件
EventBus.getDefault().post(new MessageEvent("Hello everyone!"));深入了解
1.ThreadMode线程通信
EventBus可以很简单的实现线程间的切换,包括后台线程、UI线程、异步线程
ThreadMode.POSTING
//默认调用方式,在调用post方法的线程执行,避免了线程切换,性能开销最少
// Called in the same thread (default)
@Subscribe(threadMode = ThreadMode.POSTING)
public void onMessage(MessageEvent event) {
log(event.message);
}ThreadMode.MAIN
// Called in Android UI's main thread
@Subscribe(threadMode = ThreadMode.MAIN) public void onMessage(MessageEvent event) {
textField.setText(event.message);
}ThreadMode.BACKGROUND
// 如果调用post方法的线程不是主线程,则直接在该线程执行
// 如果是主线程,则切换到后台单例线程,多个方法公用同个后台线程,按顺序执行,避免耗时操作
// Called in the background thread
@Subscribe(threadMode = ThreadMode.BACKGROUND) public void onMessage(MessageEvent event){
saveToDisk(event.message);
}ThreadMode.ASYNC
//开辟新独立线程,用来执行耗时操作,例如网络访问
//EventBus内部使用了线程池,但是要尽量避免大量长时间运行的异步线程,限制并发线程数量
//可以通过EventBusBuilder修改,默认使用Executors.newCachedThreadPool()
// Called in a separate thread
@Subscribe(threadMode = ThreadMode.ASYNC) public void onMessage(MessageEvent event){
backend.send(event.message);
}2.配置EventBusBuilder
EventBus提供了很多配置,一般的情况下我们可以不用配置.但是,如果你有一些其他要求,比如控制日志在开发的时候输出,发布的时候不输出,在开发的时候错误崩溃,而发布的时候不崩溃...等情况。
EventBus提供了一个默认的实现,但不是单例。
EventBus eventBus = new EventBus();
EventBus eventBus = EventBus.builder().build();
EventBus.builder().throwSubscriberException(BuildConfig.DEBUG).installDefaultEventBus();3.StickyEvent
StickyEvent在内存中保存最新的消息,取消原有消息,执行最新消息,只有在注册后才会执行,如果没有注册,消息会一直保留来内存中
EventBus.getDefault().postSticky(new MessageEvent("Hello everyone!"));
@Override
public void onStart() { super.onStart();
EventBus.getDefault().register(this);
}
@Subscribe(sticky = true, threadMode = ThreadMode.MAIN) public void onEvent(MessageEvent event) {
textField.setText(event.message);
} @Override
public void onStop() {
EventBus.getDefault().unregister(this); super.onStop();
}你也可以手动管理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
}在这里,或许你会有个疑问,
StickyEvent=true的订阅者能否接收post的事件?
StickyEvent=false的订阅者能否接收postSticky的事件?
查看源码发现
public void postSticky(Object event) {
synchronized (stickyEvents) {
stickyEvents.put(event.getClass(), event);
}
post(event);
} private void subscribe(Object subscriber, SubscriberMethod subscriberMethod) {
...省略部分代码 if (subscriberMethod.sticky) { if (eventInheritance) {
Set, Object>> entries = stickyEvents.entrySet(); for (Map.Entry, Object> entry : entries) {
Class candidateEventType = entry.getKey(); if (eventType.isAssignableFrom(candidateEventType)) { Object stickyEvent = entry.getValue();
checkPostStickyEventToSubscription(newSubscription, stickyEvent);
}
}
} else { Object stickyEvent = stickyEvents.get(eventType);
checkPostStickyEventToSubscription(newSubscription, stickyEvent);
}
}
} boolean checkAdd(Method method, Class eventType) {
Object existing = anyMethodByEventType.put(eventType, method); if (existing == null) { return true;
} else { if (existing instanceof Method) { if (!checkAddWithMethodSignature((Method) existing, eventType)) {
throw new IllegalStateException();
}
anyMethodByEventType.put(eventType, this);
} return checkAddWithMethodSignature(method, eventType);
}
}发现,post方法没有过滤StickyEvent,而postSticky是调用post方法的,所以,无论post还是postSticky,StickyEvent是true或false,都会执行
4.priority事件优先级
@Subscribe(priority = 1); public void onEvent(MessageEvent event) {
…
}
int size = subscriptions.size(); for (int i = 0; i <= size;="" i++)="" {="" ="" if="" (i="=" size="" ||="" subscribermethod.priority=""> subscriptions.get(i).subscriberMethod.priority) {
subscriptions.add(i, newSubscription); break;
}
}5.中止事件传递
@Subscribe public void onEvent(MessageEvent event){
…
EventBus.getDefault().cancelEventDelivery(event) ;
}6.index索引加速
EventBus使用了annotation,默认在编译时生成代码,生成索引,
添加index后会在编译时运行,自动生成相应代码。
ps:由于apt的限制,匿名内部类中的annotation不会被识别,会自动降级在运行时反射,此时,效率会降低
buildscript {
dependencies {
classpath 'com.neenbedankt.gradle.plugins:android-apt:1.8'
}
}
apply plugin: 'com.neenbedankt.android-apt'dependencies {
compile 'org.greenrobot:eventbus:3.0.0'
apt 'org.greenrobot:eventbus-annotation-processor:3.0.1'}
apt { arguments {
eventBusIndex "com.example.myapp.MyEventBusIndex"
}
}EventBus eventBus = EventBus.builder().addIndex(new MyEventBusIndex()).build();EventBus.builder().addIndex(new MyEventBusIndex()).installDefaultEventBus();EventBus eventBus = EventBus.getDefault();7.NoSubscriberEvent
如果没找到订阅者事件,可以通过EventBusBuilder设置是否默认发送NoSubscriberEvent,默认是打开的
private void postSingleEvent(Object event, PostingThreadState postingState) throws Error {
.... if (!subscriptionFound) { if (logNoSubscriberMessages) {
Log.d(TAG, "No subscribers registered for event " + eventClass);
} if (sendNoSubscriberEvent && eventClass != NoSubscriberEvent.class &&
eventClass != SubscriberExceptionEvent.class) {
post(new NoSubscriberEvent(this, event));
}
}
}8.混淆
-keepattributes *Annotation*
-keepclassmembers class ** {
@org.greenrobot.eventbus.Subscribe ;
}
-keep enum org.greenrobot.eventbus.ThreadMode { *; }
# Only required if you use AsyncExecutor-keepclassmembers class * extends org.greenrobot.eventbus.util.ThrowableFailureEvent {
(java.lang.Throwable);
}9.利弊
简单,方便,小巧,文档清晰,性能消耗少,可定制行强,耦合度低
耦合度太低
这绝对不是个笑话,,EventBus的耦合太低了,如果不加以控制管理,你会不知道,你发的消息到跑哪里去了。也不知道你的这条消息,会在哪里发出。如果你没有很好的方法解决这个问题,建议不好用太多。
使用建议
1、EventBus管理
EventBus运行创建多个,那么,明确事件的生命周期,根据不同生命周期使用不同的EventBus?
public class EventBusFactory { private static SparseArray mBusSparseArray = new SparseArray<>(2); @IntDef({CREATE, START}) @Retention(RetentionPolicy.SOURCE) public @interface BusType {
} public static final int CREATE = 0; public static final int START = 1; static {
mBusSparseArray.put(CREATE, EventBus.builder().build());
mBusSparseArray.put(START, EventBus.getDefault());
} public static EventBus getBus() { return getBus(START);
} public static EventBus getBus(@BusType int type) { return mBusSparseArray.get(type);
}
}public enum EventBusFactory {
CREATE(0),
START(1); private int mType;
EventBusFactory(int type) {
mType = type;
} public EventBus getBus() { return mBusSparseArray.get(mType);
} private static SparseArray mBusSparseArray = new SparseArray<>(2); static {
mBusSparseArray.put(CREATE.mType, EventBus.builder().build());
mBusSparseArray.put(START.mType, EventBus.getDefault());
}
}2、以事件为对象
将数据封装到一个事件类。所有事件放到一个包下。如果事件太多,同个模块的事件可以考虑使用静态内部类,或者再分包。
public final class NoSubscriberEvent {
public final EventBus eventBus;
public final Object originalEvent; public NoSubscriberEvent(EventBus eventBus, Object originalEvent) {
this.eventBus = eventBus;
this.originalEvent = originalEvent;
}
}public class Event {
public static class UserListEvent {
public List users ;
} public static class ItemListEvent {
public List items;
}
}注意,不是相同类型就一定要作为一个事件封装,具体需要考虑业务情景跟代码情况,比如事件行为不同、事件生命周期不同,如果有必要,写封装成两个Event可能是更好的选择。
public class Event {
public static class UserListUpdateEventOnCreate {
public List users;
}
public static class UserListUpdateEventOnStart {
public List users ;
} public static class UserListRemoveEventOnStart {
public List users;
}
}参考文献
EventBus官网地址
EventBus github地址
EventBus 3.0的用法详解