美文网首页
图解EventBus源码

图解EventBus源码

作者: 白六小子 | 来源:发表于2018-08-12 22:04 被阅读109次
    观察者模式和订阅发布模式预览

    开局一张图,故事全靠编。(看不见水印,看不见水印,看不见水印)

    1.EventBus源于订阅发布模式。

    在订阅发布模式(上图右)中,发布者和订阅着互相不感知对方的存在,双方通过消息代理进行通信,各组件间松耦合。Eventbus的作用正如上图的EventChannel,它提供的功能更是一种总线机制,甚至可以说是路由机制。发布者将消息发布到总线Eventbus上,剩下的工作交有Eventbus来处理。订阅者被EventBus持有和维护,EventBus将消息一一发布给订阅者。正因此,我们项目中,一版可以显示找到订阅者入activity,但是发布者则隐藏于各种,随处都可以是发布者,随处都可以post出来消息。

    而在观察者模式(上图左)中,观察者和被观察者没有完全解耦,抽象被观察者持有抽象观察者,并维护观察者列表。与订阅模式相当于在观察者和被观察者之间架了一层,由这个中间层来持有观察者,这样被观察者无须感知观察者。

    订阅发布模式 观察者模式 EventBus核心结构

    2.register过程

    image5.png

    通过findSubscriberMethods查找回来一个包含订阅者所有订阅方法的订阅列表。跟进findSubscriberMethods如何查找。

    image6.png

    通过运行时反射遍历查找

    image7.png image8.png

    如果设置了索引加速,通过索引查找。原理是EventBusAnnotationProcessor。

    image9.png

    找打订阅方法后,开始进行一系列的存储操作

    image10.png image11.png

    如果是黏性属性的方法,则立即去缓存黏性事件的stickyEvents查找是否有黏性事件,有则立即post到当前订阅者里执行,且只有当前订阅者会执行一次,其他地方不会执行,也没有查找订阅者这一过程。

    register小结

    register过程 核心数据结构

    3.post过程

    image14.png image15.png image16.png image17.png

    ThreaMode说明

    PostThread:默认的 ThreadMode,表示在执行 Post 操作的线程直接调用订阅者的事件响应方法(哪个线程post出来就在哪个线程中执行),不论该线程是否为主线程(UI 线程)。当该线程为主线程时,响应方法中不能有耗时操作,否则有卡主线程的风险。适用场景:对于是否在主线程执行无要求,但若 Post 线程为主线程,不能耗时的操作;

    MainThread:在主线程中执行响应方法。如果发布线程就是主线程,则直接调用订阅者的事件响应方法,否则通过主线程的 Handler 发送消息在主线程中处理——调用订阅者的事件响应函数。显然,MainThread类的方法也不能有耗时操作,以避免卡主线程。适用场景:必须在主线程执行的操作;

    BackgroundThread:在后台线程中执行响应方法。如果发布线程不是主线程,则直接调用订阅者的事件响应函数,否则启动唯一的后台线程去处理。由于后台线程是唯一的,当事件超过一个的时候,它们会被放在队列中依次执行,因此该类响应方法虽然没有PostThread类和MainThread类方法对性能敏感,但最好不要有重度耗时的操作或太频繁的轻度耗时操作,以造成其他操作等待。适用场景:操作轻微耗时且不会过于频繁,即一般的耗时操作都可以放在这里;

    Async:不论发布线程是否为主线程,都使用一个空闲线程来处理。和BackgroundThread不同的是,Async类的所有线程是相互独立的,因此不会出现卡线程的问题。适用场景:长耗时操作,例如网络访问。

    再来看看黏性事件的post过程

    image18.png

    注意看,这里有个加锁的过程,为什么postSticky需要加锁,而post不需要加锁?因为postSticky的事件会先存储到Map<Class<?>, Object> stickyEvents中,而stickyEvents是EventBus全局只有一个

    ,也就是所有的黏性事件都存储在这个map中,故当不同线程同时poststicky事件的时候存在并发问题。而普通的post出来的事件,都是存储在线程的本地变量的事件队列里,各线程互不干扰互相不能访问对方的数据,故不存在并发问题。

    post小结

    post过程 image20.png

    ThreadLocal:线程用来存储私有变量(一个很有意思的东西)

    名义上以ThreadLocalMap变量形式在线程内部,但是底层实现是基于Entry[]数组而不是HashMap。(这里引发一个思考,为什么要用数组实现,其实数组也可以称一种map,用数组实现开放定址法处理冲突,

    用数组存储key和value更节省内存,普通的HashMap是拉链法解决冲突,基于数组和链表,每个Entry元素除了key和value还要一个Entry类型的next指针,占用更多内存。同样的思想也在Android的sparseArray和ArrayMap中使用。)

    Entry{

    Object key; // key实际上是我们定义的ThreadLocal对象,而非当前线程对象

    Object value;

    }

    image21.png

    4.unregister

    image22.png image23.png image24.png

    5思考&总结

    优点:

    1.事件总线通信,使用简单

    2.解耦,干脆利落

    缺点:

    1.极致的解耦导致项目维护和阅读难度增大,出现EventBus满天飞的场景

    2.更甚者,它使我们往往懒于去代码中找寻设计的快感

    3.如果不及时unregister则会内存泄露

    关于性能:

    1.EventBus3.0以前大量使用反射,存在性能瓶颈,3.0以后引入APT(注解处理器)后在编译期解析注解,性能飙升

    2.EventBus在每次查找到订阅者和其绑定的订阅方法集合后会放在Map里缓存。(这是注解框架的一贯套路,Butterknife亦如是)

    参考文章:
    http://www.cnblogs.com/bugly/p/5475034.html

    相关文章

      网友评论

          本文标题:图解EventBus源码

          本文链接:https://www.haomeiwen.com/subject/cyeebftx.html