##写在前面java
###1.前言 曾经,一层又一层的业务逻辑让我不知所措,一个又一个的回调让你头晕眼花,一个又一个的参数让你混乱不堪。EventBus,,一个耦合度低到令你惧怕的框架。 ###2.什么是EventBus EventBus是一个消息总线,以观察者模式实现,用于简化程序的组件、线程通讯,能够轻易切换线程、开辟线程。EventBus3.0跟先前版本的区别在于加入了annotation @Subscribe,取代了之前约定命名的方式。 ###3.类似产品比较android
产品名 | 开发者 | 备注 |
---|---|---|
EventBus | greenrobot | 用户最多,简洁,方便,小巧,文档简洁明了 |
Guava | 一个庞大的工具类库,EventBus只是一个小功能 | |
otto | square | fork guava ,用的人很多 |
AndroidEventBus | 何红辉 | 模仿EventBus开发的 |
##使用EventBus3.0三部曲git
###1.定义事件github
public class MessageEvent { public final String message; public MessageEvent(String message) { this.message = message; } }
###2.准备订阅者segmentfault
// This method will be called when a MessageEvent is posted @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); }
@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) // ThreadMode is optional here public void onMessage(MessageEvent event) { log(event.message); }
####ThreadMode.MAINapp
// 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.getDefault()以前配置,且只能设置一次。建议在application.onCreate()调用 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); } //在onStart调用register后,执行消息 @Subscribe(sticky = true, threadMode = ThreadMode.MAIN) public void onEvent(MessageEvent event) { // UI updates must run on MainThread 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的事件? 查看源码发现
/** * Posts the given event to the event bus and holds on to the event (because it is sticky). The most recent sticky * event of an event's type is kept in memory for future access by subscribers using {@link Subscribe#sticky()}. */ 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); }
private void subscribe(Object subscriber, SubscriberMethod subscriberMethod) { ...省略部分代码 if (subscriberMethod.sticky) { if (eventInheritance) { // Existing sticky events of all subclasses of eventType have to be considered. // Note: Iterating over all events may be inefficient with lots of sticky events, // thus data structure should be changed to allow a more efficient lookup // (e.g. an additional map storing sub classes of super classes: Class -> List<Class>). Set<Map.Entry<Class<?>, Object>> entries = stickyEvents.entrySet(); for (Map.Entry<Class<?>, 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) { // 2 level check: 1st level with event type only (fast), 2nd level with complete signature when required. // Usually a subscriber doesn't have methods listening to the same event type. Object existing = anyMethodByEventType.put(eventType, method); if (existing == null) { return true; } else { if (existing instanceof Method) { if (!checkAddWithMethodSignature((Method) existing, eventType)) { // Paranoia check throw new IllegalStateException(); } // Put any non-Method object to "consume" the existing Method anyMethodByEventType.put(eventType, this); } return checkAddWithMethodSignature(method, eventType); } }
发现,post方法没有过滤StickyEvent,而postSticky是调用post方法的,因此,不管post仍是postSticky,StickyEvent是true或false,都会执行 ###4.priority事件优先级
//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(); // Now the default instance uses the given index. Use it like this: 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 <methods>; } -keep enum org.greenrobot.eventbus.ThreadMode { *; } # Only required if you use AsyncExecutor -keepclassmembers class * extends org.greenrobot.eventbus.util.ThrowableFailureEvent { <init>(java.lang.Throwable); }
###9.利弊 好处
简单,方便,小巧,文档清晰,性能消耗少,可定制行强,耦合度低
坏处
耦合度过低
这绝对不是个笑话,,EventBus的耦合过低了,若是不加以控制管理,你会不知道,你发的消息到跑哪里去了。也不知道你的这条消息,会在哪里发出。若是你没有很好的方法解决这个问题,建议很差用太多。
##使用建议
###一、EventBus管理 EventBus运行建立多个,那么,明确事件的生命周期,根据不一样生命周期使用不一样的EventBus?
/** * 方法1 * 用annotation配合使用工厂 * EventBusFactory.getBus(EventBusFactory.START); * EventBusFactory.getBus(); */ public class EventBusFactory { private static SparseArray<EventBus> 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); } }
/** * 方法2 * 用枚举工厂 * EventBusFactory.START.getBus(); */ 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<EventBus> mBusSparseArray = new SparseArray<>(2); static { mBusSparseArray.put(CREATE.mType, EventBus.builder().build()); mBusSparseArray.put(START.mType, EventBus.getDefault()); } }
###二、以事件为对象 将数据封装到一个事件类。全部事件放到一个包下。若是事件太多,同个模块的事件能够考虑使用静态内部类,或者再分包。
/** * This Event is posted by EventBus when no subscriber is found for a posted event. * * @author Markus */ public final class NoSubscriberEvent { /** The {@link EventBus} instance to with the original event was posted to. */ public final EventBus eventBus; /** The original event that could not be delivered to any subscriber. */ 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<User> users ; } public static class ItemListEvent { public List<Item> items; } }
注意,不是相同类型就必定要做为一个事件封装,具体须要考虑业务情景跟代码状况,好比事件行为不一样、事件生命周期不一样,若是有必要,写封装成两个Event多是更好的选择。
public class Event { public static class UserListUpdateEventOnCreate { public List<User> users; } public static class UserListUpdateEventOnStart { public List<User> users ; } public static class UserListRemoveEventOnStart { public List<User> users; } }
##参考文献
##说了这么多废话,,下面进入正题