EventBus & Otto

来源:互联网 发布:电信网络拓扑分析 编辑:程序博客网 时间:2024/05/22 01:45

一、基本介绍

EventBus 是一个 Android 事件发布/订阅框架,通过解耦发布者和订阅者简化 Android 事件传递,这里的事件可以理解为消息,本文中统一称为事件。事件传递既可用于 Android 四大组件间通讯,也可以用户异步线程和主线程间通讯等等。EventBus

EventBus3.0版本有较大的更新,性能上有很大提升。这里主要介绍新版本。

传统的事件传递方式包括:Handler、BroadCastReceiver、Interface 回调,相比之下 EventBus 的优点是代码简洁,使用简单,并将事件发布和订阅充分解耦。类似框架OTTO。

二、EventBus & Otto对比

共同点

1、都是事件总线框架,满足消息/事件传递的同时,也实现了组件间的解耦.
2、注册的共同点都是采用method方法进行一个集成。

3、都采用注解的方式来标注订阅方法(旧版本的EventBus通过固定方法名标记订阅者)

4、大部分规则相同,比如订阅方法只能有一个参数。

5、都不适用进程间通信

不同点

1、OTTO更加轻量级,结构简单。EventBus稍微复杂一些。

2、OTTO默认在主线程中使用,不能在其他线程使用,通过设置ThreadEnforcer可以在任意线程使用,但是消息传递不能指定目标线程,EventBus实现了4种ThreadMode,线程之间消息传递非常灵活。
3、EventBus支持粘性事件,而OTTO不支持。即先发消息,再注册订阅者仍然能够收到消息。

3、OTTO有默认的生产者方法,可以产生默认消息,EventBus没有

三、EventBus的简单使用介绍

定义消息

[java] view plain copy
 print?在CODE上查看代码片派生到我的代码片
  1. public class MessageEvent {  
  2.  //定义相关属性  
  3.  }  

注册

[java] view plain copy
 print?在CODE上查看代码片派生到我的代码片
  1. eventBus.register(this);  

定义订阅者

[java] view plain copy
 print?在CODE上查看代码片派生到我的代码片
  1. @Subscribe  
  2. public void onEvent(MessageEvent event) {  
  3. //收到消息后的处理  
  4. };  

发送消息

[java] view plain copy
 print?在CODE上查看代码片派生到我的代码片
  1. eventBus.post(event);  

解绑

[java] view plain copy
 print?在CODE上查看代码片派生到我的代码片
  1. eventBus.unregister(this);  

四、源码解析

1、EventBus构造

通常我们调用EventBus.getDefault()获取EventBus
[java] view plain copy
 print?在CODE上查看代码片派生到我的代码片
  1. public static EventBus getDefault() {  
  2.     if (defaultInstance == null) {  
  3.         synchronized (EventBus.class) {  
  4.             if (defaultInstance == null) {  
  5.                 defaultInstance = new EventBus();  
  6.             }  
  7.         }  
  8.     }  
  9.     return defaultInstance;  
  10. }  

这个方法是线程安全的,EventBus的无参构造方法使用默认构建器DEFAULT_BUILDER构造,EventBusBuilder指定了EventBus的一些行为,用于输出log,查错,调试等。
logSubscriberExceptions 指定了收到SubscriberExceptionEvent类型消息时抛出异常的行为,默认为true,打印log,如果为false不输出log
输出的信息有
[java] view plain copy
 print?在CODE上查看代码片派生到我的代码片
  1. Log.e(TAG, "SubscriberExceptionEvent subscriber " + subscription.subscriber.getClass()  
  2.     + " threw an exception", cause);  
  3.     SubscriberExceptionEvent exEvent = (SubscriberExceptionEvent) event;  
  4.     Log.e(TAG, "Initial event " + exEvent.causingEvent + " caused exception in "  
  5.      + exEvent.causingSubscriber, exEvent.throwable);  
  6. Log.e(TAG, "Could not dispatch event: " + event.getClass() + " to subscribing class "  
  7.       + subscription.subscriber.getClass(), cause);  

对logNoSubscriberMessages指定了发送了没有订阅者的消息的行为,默认为true,打印log,如果为false不输出log
输出的信息为
[java] view plain copy
 print?在CODE上查看代码片派生到我的代码片
  1. Log.d(TAG, "No subscribers registered for event " + eventClass);  
sendSubscriberExceptionEvent指定是否发送SubscriberExceptionEvent,默认为true
操作行为
[java] view plain copy
 print?在CODE上查看代码片派生到我的代码片
  1. SubscriberExceptionEvent exEvent =   
  2. new SubscriberExceptionEvent(this, cause, event,subscription.subscriber);  
  3. post(exEvent);  
sendNoSubscriberEvent指定了没有订阅者时的行为,默认为true,发送NoSubscriberEvent这样一个消息
[java] view plain copy
 print?在CODE上查看代码片派生到我的代码片
  1. if (sendNoSubscriberEvent && eventClass != NoSubscriberEvent.class &&  
  2.               eventClass != SubscriberExceptionEvent.class) {  
  3.         post(new NoSubscriberEvent(this, event));  
  4. }  

throwSubscriberException指定了订阅方法执行异常时是否抛出异常,默认为false
[java] view plain copy
 print?在CODE上查看代码片派生到我的代码片
  1. if (logSubscriberExceptions) {  
  2.           Log.e(TAG, "Could not dispatch event: " + event.getClass() + " to subscribing class "  
  3.                   + subscription.subscriber.getClass(), cause);  
  4. }  

eventInheritance指定是否发消息给这个事件的父事件对应的订阅者,默认为true。如果为false,只会发消息给类型完全一致的订阅者
[java] view plain copy
 print?在CODE上查看代码片派生到我的代码片
  1. if (eventInheritance) {  
  2.        List<Class<?>> eventTypes = lookupAllEventTypes(eventClass);  
  3.        int countTypes = eventTypes.size();  
  4.        for (int h = 0; h < countTypes; h++) {  
  5.            Class<?> clazz = eventTypes.get(h);  
  6.            subscriptionFound |= postSingleEventForEventType(event, postingState, clazz);  
  7.        }  
  8.    } else {  
  9.     subscriptionFound = postSingleEventForEventType(event, postingState, eventClass);  
  10. }  
ignoreGeneratedIndex指定是否忽略设置索引,这个值默认是false,这段代码中使用的两种解析方式不容易看懂
[java] view plain copy
 print?在CODE上查看代码片派生到我的代码片
  1. if (ignoreGeneratedIndex) {  
  2.     subscriberMethods = findUsingReflection(subscriberClass);  
  3.  } else {  
  4.     subscriberMethods = findUsingInfo(subscriberClass);  
  5. }  
findUsingReflection利用反射来获取订阅类中的订阅方法信息
findUsingInfo从注解器生成的MyEventBusIndex类中获得订阅类的订阅方法信息
findUsingInfo执行效率更高一些,默认使用这种。
strictMethodVerification指定是否严格校验,默认为false。严格校验会直接抛出异常
[java] view plain copy
 print?在CODE上查看代码片派生到我的代码片
  1. else if (strictMethodVerification && method.isAnnotationPresent(Subscribe.class)) {  
  2.             String methodName = method.getDeclaringClass().getName() + "." + method.getName();  
  3.             throw new EventBusException("@Subscribe method " + methodName +  
  4.                     "must have exactly 1 parameter but has " + parameterTypes.length);  
  5.             }  
  6.         } else if (strictMethodVerification && method.isAnnotationPresent(Subscribe.class)) {  
  7.             String methodName = method.getDeclaringClass().getName() + "." + method.getName();  
  8.             throw new EventBusException(methodName +  
  9.                     " is a illegal @Subscribe method: must be public, non-static, and non-abstract");  
  10.         }  

2、注册与解析

注册之后,解析出了所有的订阅方法,方法参数类型
[java] view plain copy
 print?在CODE上查看代码片派生到我的代码片
  1. public void register(Object subscriber) {  
  2.         Class<?> subscriberClass = subscriber.getClass();  
  3.         List<SubscriberMethod> subscriberMethods = subscriberMethodFinder.findSubscriberMethods(subscriberClass);  
  4.         synchronized (this) {  
  5.         for (SubscriberMethod subscriberMethod : subscriberMethods) {  
  6.             subscribe(subscriber, subscriberMethod);  
  7.         }  
  8.     }  
  9. }  

相关映射主要是两个
[java] view plain copy
 print?在CODE上查看代码片派生到我的代码片
  1. private final Map<Class<?>, CopyOnWriteArrayList<Subscription>> subscriptionsByEventType;  
  2. private final Map<Object, List<Class<?>>> typesBySubscriber;  
subscriptionsByEventType的key是订阅者消息类型,value是Subscription,是具体订阅者对应注册对象和方法
typesBySubscriber的key是注册对象,value是对应所有订阅者消息类型的list
解析器是SubscriberMethodFinder这个类,EventBus中有subscriberMethodFinder这个实例,调用
List<SubscriberMethod> findSubscriberMethods(Class<?> subscriberClass)这个方法解析方法信息
如果没有订阅者方法,会抛出异常
[java] view plain copy
 print?在CODE上查看代码片派生到我的代码片
  1. throw new EventBusException("Subscriber " + subscriberClass  
  2.                 + " and its super classes have no public methods with the @Subscribe annotation");  
如果已经注册过,会抛出异常
[java] view plain copy
 print?在CODE上查看代码片派生到我的代码片
  1. throw new EventBusException("Subscriber " + subscriber.getClass() + " already registered to event "  
  2.                     + eventType);  
如果订阅者是粘性事件,如果粘性事件不为空会直接发送粘性事件。
粘性事件存储结构为
[java] view plain copy
 print?在CODE上查看代码片派生到我的代码片
  1. private final Map<Class<?>, Object> stickyEvents;  

stickyEvents的key是事件类型,Object是对应的具体事件。key,value唯一,说明粘性事件是会被覆盖的。这样可以节省内存开销。

3、发送消息

[java] view plain copy
 print?在CODE上查看代码片派生到我的代码片
  1. public void post(Object event) {  
  2.        PostingThreadState postingState = currentPostingThreadState.get();  
  3.        List<Object> eventQueue = postingState.eventQueue;  
  4.        eventQueue.add(event);  
  5.        if (!postingState.isPosting) {  
  6.            postingState.isMainThread = Looper.getMainLooper() == Looper.myLooper();  
  7.            postingState.isPosting = true;  
  8.            if (postingState.canceled) {  
  9.                throw new EventBusException("Internal error. Abort state was not reset");  
  10.            }  
  11.            try {  
  12.                while (!eventQueue.isEmpty()) {  
  13.                    postSingleEvent(eventQueue.remove(0), postingState);  
  14.                }  
  15.            } finally {  
  16.                postingState.isPosting = false;  
  17.                postingState.isMainThread = false;  
  18.            }  
  19.        }  
  20. }  

[java] view plain copy
 print?在CODE上查看代码片派生到我的代码片
  1. public void postSticky(Object event) {  
  2.     synchronized (stickyEvents) {  
  3.         stickyEvents.put(event.getClass(), event);  
  4.     }  
  5.     // Should be posted after it is putted, in case the subscriber wants to remove immediately  
  6.     post(event);  
  7. }  

post线程状态通过ThreadLocal维护,保证线程隔离,不需要加锁,提高运行效率。
[java] view plain copy
 print?在CODE上查看代码片派生到我的代码片
  1. private final ThreadLocal<PostingThreadState> currentPostingThreadState = new ThreadLocal<PostingThreadState>() {  
  2.     @Override  
  3.     protected PostingThreadState initialValue() {  
  4.         return new PostingThreadState();  
  5.     }  
  6. };  


经过以下四个方法,事件分发到对应线程的对应订阅者
private void postSingleEvent(Object event, PostingThreadState postingState) 
private boolean postSingleEventForEventType(Object event, PostingThreadState postingState, Class<?> eventClass)
private void postToSubscription(Subscription subscription, Object event, boolean isMainThread)
void invokeSubscriber(Subscription subscription, Object event) 
特别注意线程处理
[java] view plain copy
 print?在CODE上查看代码片派生到我的代码片
  1. switch (subscription.subscriberMethod.threadMode) {  
  2.            case POSTING:  
  3.                invokeSubscriber(subscription, event);  
  4.                break;  
  5.            case MAIN:  
  6.                if (isMainThread) {  
  7.                    invokeSubscriber(subscription, event);  
  8.                } else {  
  9.                    mainThreadPoster.enqueue(subscription, event);  
  10.                }  
  11.                break;  
  12.            case BACKGROUND:  
  13.               if (isMainThread) {  
  14.                    backgroundPoster.enqueue(subscription, event);  
  15.                } else {  
  16.                    invokeSubscriber(subscription, event);  
  17.                }  
  18.                break;  
  19.            case ASYNC:  
  20.                asyncPoster.enqueue(subscription, event);  
  21.                break;  
  22.            default:  
  23.                throw new IllegalStateException("Unknown thread mode: " + subscription.subscriberMethod.threadMode);  
  24.        }  

POSTING:是在当前线程执行
MAIN:抛到主线程
BACKGROUND:如果是在主线程,抛到新线程,如果不是在主线程,就在当前线程执行
ASYNC:抛到新线程中
没有订阅者的消息默认会被重新包装为NoSubscriberEvent,可以做一些全局捕获处理

4、解绑

[java] view plain copy
 print?在CODE上查看代码片派生到我的代码片
  1. public synchronized void unregister(Object subscriber) {  
  2.     List<Class<?>> subscribedTypes = typesBySubscriber.get(subscriber);  
  3.     if (subscribedTypes != null) {  
  4.         for (Class<?> eventType : subscribedTypes) {  
  5.             unsubscribeByEventType(subscriber, eventType);  
  6.         }  
  7.         typesBySubscriber.remove(subscriber);  
  8.     } else {  
  9.         Log.w(TAG, "Subscriber to unregister was not registered before: " + subscriber.getClass());  
  10.     }  
  11. }  
做一些清理工作,注意不要重复解绑。


刚开始已经说过了,EventBus跟OTTO很像,而 Otto 是基于 Guava 的增强的事件总线,Guava 是google一个很强大的java开源库。EventBus改进是最多的,据说是效率最高的,这个没有做过相关测试。但是从实现上来看,对性能优化处理还是做了很多事情。

0 0
原创粉丝点击