Android学习笔记—— EventBus 源码解析

最近项目里用到的EventBus 更新到了 3.0,学习了一下EventBus 3.0 的源码,发现3.0版本较之前版本变化还是相对较大的,无论从性能上,还是使用流程上。恰好组内分享轮到我,所以就做了一次EventBus 3.0的源码解析。


Android学习笔记—— EventBus 源码解析_第1张图片 

 

 EventBus 是 事件发布/订阅 总线,是一种超低耦合的消息传递方式,很多项目都在用它啦,用法就不讲了,从源码里讲讲具体实现,顺带讲讲用它的时候需要注意的地方。


 EventBus 从使用流程上来说,大致可分为三步:register, post, unregister。下面就分布来看看EventBus的源码。


一. register


Android学习笔记—— EventBus 源码解析_第2张图片


Android学习笔记—— EventBus 源码解析_第3张图片


 该方法首先获取获取传进来参数的Class对象,然后根据这个参数通过subscriberMethodFinder.findSubscriberMethods方法获取所有的事件处理方法。老的版本里还有对传进来的类是否是匿名类的判断,3.0正式版兼容了对于匿名类的处理。另外要说的是,从3.0版本开始,统一了register 入口,之前版本还有对register的重载入口以及registerSticky入口等,有些复杂而且在是否sticky上容易出错。


Android学习笔记—— EventBus 源码解析_第4张图片


可以见到在 3.0 beta版本里,作者已经在着手加入index以减少遍历订阅方法时的性能消耗了,但是真正加入索引查找是在 3.0的正式版本里。


Android学习笔记—— EventBus 源码解析_第5张图片


我们可以看出,在3.0beta 和 3.0两个版本中,该函数的实现过程还是大有不同的。加上索引查找以后,利用索引查找以及缓存查找以及能找到大部分订阅方法,这样就大大减小了进入 findUsingReflection()方法的查找流量,毕竟在该方法里,查找到我们所要的订阅方法集合,我们需要用反射的方法去遍历每一个类的每个方法,然后找出订阅该事件的订阅方法,很耗时,可能会ANR.


Android学习笔记—— EventBus 源码解析_第6张图片


为了能使3.0的EventBus 产生索引,需在项目 build.gradle 中加入 图中第二句的依赖。


Android学习笔记—— EventBus 源码解析_第7张图片


Android学习笔记—— EventBus 源码解析_第8张图片


找到事件处理函数后,会遍历找到的所有事件处理函数并调用subscribe方法将所有事件处理函数注册到EventBus.里主要注意checkPostStickyEventToSubscription这个方法,这个方法是统一register入口的关键,它处理了sticky事件。从而可以在3.0中去掉registerSticky方法。可以看到,如果是黏性事件,会执行postToSubscription方法,这就该讲post事件的具体实现流程了。


二. post


Android学习笔记—— EventBus 源码解析_第9张图片


在post方法中,首先会将event对象添加到事件队列eventQueue中。然后判断是否有事件正在post,如果没有则会遍历eventQueue中每一个event对象,并且调用postSingleEvent方法post该事件。而postSticky 是在执行post之前缓存传来的event 在一个 stickyEvents 列表里。


Android学习笔记—— EventBus 源码解析_第10张图片


eventInheritance标识该事件是否可以被继承,若为true则会去查找所有该事件的父类和接口类,并将其保存到eventTypesCache中,方便下次使用。不管允不允许事件继承,都会执行postSingleEventForEventType方法post事件。


Android学习笔记—— EventBus 源码解析_第11张图片


在postSingleEventForEventType方法中,会已eventClass为key从subscriptionsByEventType对象中获取Subscription列表。在上面讲register的时候我们已经看到EventBus在register的时候会将Subscription列表存储在subscriptionsByEventType中。接下来会遍历subscriptions列表然后调用postToSubscription方法进行下一步处理。这里的postToSubscription就接上了 在 register 流程里所讲的,当事件为sticky事件时,会执行postToSubscription 方法将该事件post出去。。而参数则来自于缓存list stickyEvents,详见 register流程 subscribe 函数;


Android学习笔记—— EventBus 源码解析_第12张图片


该方法主要是根据register注册的事件处理函数的线程模型在指定的线程中触发事件处理函数。mainThreadPoster、backgroundPoster和asyncPoster分别是HandlerPoster、BackgroundPoster和AsyncPoster的对象,其中HandlerPoster继承自Handle,BackgroundPoster和AsyncPoster继承自Runnable。这里就要说到EventBus中的四中线程模型了:


Android学习笔记—— EventBus 源码解析_第13张图片


PostThread:如果使用事件处理函数指定了线程模型为PostThread,那么该事件在哪个线程发布出来的,事件处理函数就会在这个线程中运行,也就是说发布事件和接收事件在同一个线程。在线程模型为PostThread的事件处理函数中尽量避免执行耗时操作,因为它会阻塞事件的传递,甚至有可能会引起ANR。
MainThread:如果使用事件处理函数指定了线程模型为MainThread,那么不论事件是在哪个线程中发布出来的,该事件处理函数都会在UI线程中执行。该方法可以用来更新UI,但是不能处理耗时操作。
BackgroundThread:如果使用事件处理函数指定了线程模型为BackgroundThread,那么如果事件是在UI线程中发布出来的,那么该事件处理函数就会在新的线程中运行,如果事件本来就是子线程中发布出来的,那么该事件处理函数直接在发布事件的线程中执行。在此事件处理函数中禁止进行UI更新操作。
Async:如果使用事件处理函数指定了线程模型为Async,那么无论事件在哪个线程发布,该事件处理函数都会在新建的子线程中执行。同样,此事件处理函数中禁止进行UI更新操作。


三. unregister

Android学习笔记—— EventBus 源码解析_第14张图片

OK, 最后的 unregister 就不用多说了,基本就是将subscribe 从list 里移除 从而移除订阅者和订阅方法的这么一个过程。


四. 关于sticky 的一些注意事项


总结来说呢,如果一个事件是sticky的,那么可以允许该事件先被post,再被register,而非sticky 事件是不可以的哈。

举个例子,在 A里 register 了 event,并写好了 eventA 的接收函数 onEventA(),标明 sticky = true or false,现在A跳转到了B,在B里post 了 eventA,然后back 会跳A,正常情况下onEventA()是能够接收到 eventA的post的,但在特殊情况下,A跳转完成后由于内存不足,被回收,这时候A 里的 event 会被 unregister,如果eventA sticky= true,B回退A时,A 会再次 register event,因为eventA sticky= true ,所以在register过程中会 post,所以 B 中的onEventA()会收到 eventA(其实这个eventA 已然不是 B post 的了,而是A 在 register event 时 post 的);如果eventA sticky= false,则这种情况下的回退,A中的onEventA() 是收不到 B post 来的 eventA的。。

好绕啊,不过看了源码其实挺好懂的,所以在用的时候一定注意 event的线程模型选择以及 sticky 值,也不能把sticky全置为 true,毕竟sticky 事件需要用list存储,存太多必然消耗性能。



你可能感兴趣的:(Android,学习笔记)