写在前面 EventBus一直都有听说过,但是之前都没有用过。因为自己的项目里并没有特别复杂的事件传递,自己写个接口回调弄一下解决需求就ok了。最近有需求要了解一下EventBus,将自己的所得记录一下。
基本使用 首先在app的build.gradle中添加引用代码:
1 compile 'org.greenrobot:eventbus:3.0.0' 
 
这样就可以在项目中使用EventBus了。
 
这里简单的测试一下post普通的事件和粘性事件,简单的介绍一下我所使用的测试例,有两个Activity,MainActivity和SecondActivity。使用步骤如下:
官方的介绍中是在Activity的onStart和onStop这两个生命周期中调用了register和unregister,这个根据你自己的需求。如果你与需要在Activity不可见时也要能接收到事件,那么无疑在onDestroy中unregister更好。
1 2 3 4 5 6 7 8 9 10 11 @Override protected  void  onStart ()   {    super .onStart();     EventBus.getDefault().register(this ); } @Override protected  void  onStop ()   {    super .onStop();     EventBus.getDefault().unregister(this ); } 
 
接着定义要传递的事件,这里只是简单的测试一下,所以只简单的复写了一下toString方法
1 2 3 4 5 6 public  static  class  MessageEvent   {    @Override      public  String toString ()   {         return  "onMessageEvent" ;     } } 
 
由于SecondActivity在MainActivity之后启动,所以需要poststicky(粘性)事件:
1 EventBus.getDefault().postSticky(new  MessageEvent()); 
 
在SecondActivity中使用@Subscribe注解标注一个方法,参数为刚才定义的事件:
1 2 3 4 @Subscribe public  void  onMessageEvent (MainActivity.MessageEvent e)   {    Log.e(TAG, e.toString()); } 
 
整个EventBus的使用流程就是这样了,当然,不要急着去试,你直接用上面的代码肯定是不行的,为什么呢?可以看一下@Subscribe这个注解的源码:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 @Documented @Retention(RetentionPolicy.RUNTIME) @Target({ElementType.METHOD}) public  @interface  Subscribe {    ThreadMode threadMode ()  default  ThreadMode.POSTING ;          boolean  sticky ()  default  false  ;          int  priority ()  default  0 ; } 
 
可以看到有个sticky()方法默认返回值是false,而上面的注释也说了为真的时候会分发sticky event。有一点也要注意,这个注解上面的@Retention(RetentionPolicy.RUNTIME)注解说明这个Subscribe注解不仅被保存到class文件中,jvm加载class文件之后,仍然存在。如果你足够了解反射和注解,你就能想到EventBus肯定是利用反射来调用注解方法的。
说了这么多,正确的代码是怎样的呢?
1 2 3 4 @Subscribe(sticky = true) public  void  onMessageEvent (MainActivity.MessageEvent e)   {    Log.e(TAG, e.toString()); } 
 
输出:
可以看到的确是接收到了事件。基本的使用方式就是这样。
Android需要在主线程更新UI,那么EventBus如何切换线程?
1 2 3 4 @Subscribe(threadMode = ThreadMode.MAIN) public  void  onMessage (MessageEvent event)   {    textField.setText(event.message); } 
 
在注解中声明threadMode = ThreadMode.Main就可以了,这样你就可以在接收到事件的时候更新UI了。ThreadMode还有POSTING、BACKGROUND和ASYNC这几个值。POSTING是默认值,就是不切换线程,直接发送事件。
更多关于EventBus的使用可以参考官方文档:http://greenrobot.org/eventbus/documentation/ 
EventBus是如何工作的? EventBus使用起来是比较简单的,刚在上面也说了EventBus通过注解配合反射来实现传递事件的功能,接下来就简要分析一下post()的流程。EventBus中的post方法里首先做了一些简单的操作和判断,接着调用了postSingleEvent()方法:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 private  void  postSingleEvent (Object event, PostingThreadState postingState)  throws  Error  {    Class<?> eventClass = event.getClass();     boolean  subscriptionFound = false ;     if  (eventInheritance) {         List<Class<?>> eventTypes = lookupAllEventTypes(eventClass);         int  countTypes = eventTypes.size();         for  (int  h = 0 ; h < countTypes; h++) {             Class<?> clazz = eventTypes.get(h);             subscriptionFound |= postSingleEventForEventType(event, postingState, clazz);         }     } else  {         subscriptionFound = postSingleEventForEventType(event, postingState, eventClass);     }     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));         }     } } 
 
可以看到首先拿到了event的Class对象,接着判断eventInheritance值,这个值默认是true,那么接着就调用了lookupAllEventTypes方法,这个方法不具体分析代码,他的功能是将传入event的所有父类或者接口的Class对象添加到一个集合中。他这样做的原因我认为是考虑到了多态问题,用户在用的时候可能会用父类Event作为注解方法的参数,而post的时候则post子类或者接口实现类之类的东西。接着遍历拿到的Class对象集合,调用postSingleEventForEventType方法。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 private  boolean  postSingleEventForEventType (Object event, PostingThreadState postingState, Class<?> eventClass)   {    CopyOnWriteArrayList<Subscription> subscriptions;     synchronized  (this ) {         subscriptions = subscriptionsByEventType.get(eventClass);     }     if  (subscriptions != null  && !subscriptions.isEmpty()) {         for  (Subscription subscription : subscriptions) {             postingState.event = event;             postingState.subscription = subscription;             boolean  aborted = false ;             try  {                 postToSubscription(subscription, event, postingState.isMainThread);                 aborted = postingState.canceled;             } finally  {                 postingState.event = null ;                 postingState.subscription = null ;                 postingState.canceled = false ;             }             if  (aborted) {                 break ;             }         }         return  true ;     }     return  false ; } 
 
首先创建了一个线程安全的集合,线程安全啥的这里不展开讨论,加锁的代码中subscriptionsByEventType究竟是个什么东西呢?
1 private  final  Map<Class<?>, CopyOnWriteArrayList<Subscription>> subscriptionsByEventType;
 
光看这个并没有了解到什么,然而这是个map,只要搜一下subscriptionsByEventType.put就可以看到赋值的操作了。经过搜索,在代码中发现啊subscribe这个方法中有调用。经过进一步的搜索,发现在register方法中调用了subscribe方法。是的,我上面讲post流程直接跳过了register这个流程,现在得补回来了。
register方法中调用了SubscriberMethod的findSubscriberMethods方法,具体的实现逻辑在findUsingReflectionInSingleClass方法中:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65     private  void  findUsingReflectionInSingleClass (FindState findState)   {         Method[] methods;         try  {                          methods = findState.clazz.getDeclaredMethods();         } catch  (Throwable th) {                          methods = findState.clazz.getMethods();             findState.skipSuperClasses = true ;         }         for  (Method method : methods) {             int  modifiers = method.getModifiers();             if  ((modifiers & Modifier.PUBLIC) != 0  && (modifiers & MODIFIERS_IGNORE) == 0 ) {                 Class<?>[] parameterTypes = method.getParameterTypes();                 if  (parameterTypes.length == 1 ) {                     Subscribe subscribeAnnotation = method.getAnnotation(Subscribe.class);                     if  (subscribeAnnotation != null ) {                         Class<?> eventType = parameterTypes[0 ];                         if  (findState.checkAdd(method, eventType)) {                             ThreadMode threadMode = subscribeAnnotation.threadMode();                             findState.subscriberMethods.add(new  SubscriberMethod(method, eventType, threadMode,                                     subscribeAnnotation.priority(), subscribeAnnotation.sticky()));                         }                     }                 } else  if  (strictMethodVerification && method.isAnnotationPresent(Subscribe.class)) {                     String methodName = method.getDeclaringClass().getName() + "."  + method.getName();                     throw  new  EventBusException("@Subscribe method "  + methodName +                             "must have exactly 1 parameter but has "  + parameterTypes.length);                 }             } else  if  (strictMethodVerification && method.isAnnotationPresent(Subscribe.class)) {                 String methodName = method.getDeclaringClass().getName() + "."  + method.getName();                 throw  new  EventBusException(methodName +                         " is a illegal @Subscribe method: must be public, non-static, and non-abstract" );             }         }     } ```` 这里可以很清晰的看到就是利用了反射来拿到注解的方法。简单的了解到如何拿到注解方法,继续回到postSingleEventForEventType方法,方法遍历了subscriptions,并将subscriptions集合的元素、event和是否是主线程的布尔值作为参数传递给postToSubscription方法: ```java      private  void  postToSubscription (Subscription subscription, Object event, boolean  isMainThread)   {        switch  (subscription.subscriberMethod.threadMode) {             case  POSTING:                 invokeSubscriber(subscription, event);                 break ;             case  MAIN:                 if  (isMainThread) {                     invokeSubscriber(subscription, event);                 } else  {                     mainThreadPoster.enqueue(subscription, event);                 }                 break ;             case  BACKGROUND:                 if  (isMainThread) {                     backgroundPoster.enqueue(subscription, event);                 } else  {                     invokeSubscriber(subscription, event);                 }                 break ;             case  ASYNC:                 asyncPoster.enqueue(subscription, event);                 break ;             default :                 throw  new  IllegalStateException("Unknown thread mode: "  + subscription.subscriberMethod.threadMode);         }     } 
 
这里可以看到对不同的threadMode进行了不同的处理,最终通过反射调用了注解方法。整个流程到此就简单的过了一遍,EventBus是通过反射来工作的,不同于dagger2用的是编译时注解,EventBus用的是Java本身提供的注解和反射,对性能会有影响,不过比起其易用方便的特性来说,还是值得用的。
这里只是简单的过了一下流程,中间跳过了很多代码,也没有很深入的分析源码。EventBus的源码并不算特别多,如果各位对反射和注解比较了解,可以读一下EventBus的源码。