写在前面 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的源码。