欢迎使用CSDN-markdown编辑器
2016-06-01 15:07
531 查看
EventBus3.0详解
android EventBusEventBus30详解
写在前面
前言
什么是EventBus
相似产品比较
使用EventBus30三部曲
定义事件
准备订阅者
发送事件
深入了解
ThreadMode线程通信
ThreadModePOSTING
ThreadModeMAIN
ThreadModeBACKGROUND
ThreadModeASYNC
配置EventBusBuilder
StickyEvent
priority事件优先级
中止事件传递
index索引加速
NoSubscriberEvent
混淆
利弊
使用建议
1EventBus管理
2以事件为对象
参考文献
说了这么多废话下面进入正题
写在前面
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.准备订阅者
// 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.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.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的耦合太低了,如果不加以控制管理,你会不知道,你发的消息到跑哪里去了。也不知道你的这条消息,会在哪里发出。如果你没有很好的方法解决这个问题,建议不好用太多。
使用建议
1、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()); } }
2、以事件为对象
将数据封装到一个事件类。所有事件放到一个包下。如果事件太多,同个模块的事件可以考虑使用静态内部类,或者再分包。/** * 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; } }
参考文献
EventBus官网地址EventBus github地址
EventBus 3.0的用法详解
说了这么多废话,,下面进入正题
相关文章推荐
- 使用C++实现JNI接口需要注意的事项
- Android IPC进程间通讯机制
- Android Manifest 用法
- [转载]Activity中ConfigChanges属性的用法
- Android之获取手机上的图片和视频缩略图thumbnails
- Android之使用Http协议实现文件上传功能
- Android学习笔记(二九):嵌入浏览器
- android string.xml文件中的整型和string型代替
- i-jetty环境搭配与编译
- android之定时器AlarmManager
- android wifi 无线调试
- Android Native 绘图方法
- Android java 与 javascript互访(相互调用)的方法例子
- android 代码实现控件之间的间距
- android FragmentPagerAdapter的“标准”配置
- Android"解决"onTouch和onClick的冲突问题
- android:installLocation简析
- android searchView的关闭事件
- SourceProvider.getJniDirectories