EventBus相信大家都不陌生,所以这里偷个懒就不说具体用法了,本篇讲解以下原理性的东西,不过在阅读本博客之前建议大家阅读博主的以下两篇博客,因为本篇是在这两篇的基础上写的:
Otto源码解读
EventBus源码解析开篇
通过上面两篇博客,可以了解EventBus的工作原理,简单来说最核心就是:
1、收集应用中所有标注了@Subscribe的Method方法(当然这些方法都是属于具体的注册者对象(Subscriber)的)
2、收到事件后执行method.invoke(subscriber,event);

上述两步就是EventBus的根本的核心所在。然后围绕着这两个核心不断的扩展功能过而已,当然具体的可参考文章开头的说的两篇博客。

结合上面两条在EventBus 中设计了Subscription这个类:

class Subscription {//EventBus.register的对象final Object subscriber;//对@Subscribe注解方法 Method对象的封装final SubscriberMethod subscriberMethod;}

在EventBus发送事件到注册者执行Subscribe方法的时候就有了如下方法:

void invokeSubscriber(Subscription subscription, Object event) {subscription.subscriberMethod.method.//Method 对象invoke(subscription.subscriber, event);//反射调用}

EventBus是事件总线模式,何谓事件,在面向对象的世界中就是个new 出来的对象而已。这个事件是要用EventBus 的post发送出去的,发送给谁?当然是发送给某个对象(面向对象的世界除了对象还有啥),且这个对象的某个方法必须标注@Subsrcribe注解,就像发送邮件的,总得门牌号,而@Subsrcibe就是这个门牌号。找到了事件接收方,怎么处理“邮件”就是接收者自己的事儿了,或者在被窝里看,或者坐客厅里看,反正爱怎么干怎么干。但是凡事总有个约束,EventBus允许事件接收者用如下几个模式来对事件进行处理,即:POSTING、MAIN、MAIN_ORDER、BACKGROUND、ASYNC。

在本篇中之对BACKGROUND模式进行简单的讲解,用法如下:

@Subscribe(threadMode = ThreadMode.BACKGROUND)
public void onEventBackgroundThread(Integer event) {}

BACKGROUND即后台模式,这个后台是相对于Android主线程而言的,也是就是在非UI线程中处理这个事件,在源码中看可以很好的理解:

  //EventBus 对BACKGROUND 模式的处理case BACKGROUND:if (isMainThread) {//如果是UI线程,则开启新线程执行之。backgroundPoster.enqueue(subscription, event);} else {//非UI线程则在直接在本线程中执行invokeSubscriber(subscription, event);}break;

从源码上也能看出BACKGROUND和ASYNC的简单区别(当然这只是区别之一):

case ASYNC://该模式下不论是UI还是非UI线程下,都另起线程执行asyncPoster.enqueue(subscription, event);

发送事件是一个具体的行为,为此EventBus抽象出来一个Poster接口出来;

interface Poster {//subscription:事件接收者//event:具体的事件void enqueue(Subscription subscription, Object event);
}

在这里说一个题外话,通常我们都说面向抽象编程,什么是面向抽象呢?简单的来说就是面向接口(interface),而不是面向实现(class)去思考。优先考虑做什么,而不是如何做。比如你去打印文件,你要做的就是打印文件这件事。抽象一个PrintFile接口,而不是先考虑如何打印。

 interface PritFile{void print();}

至于是用公司(CompanyPrinter)的打印机去打印,还是去打印店(ShopPrinter),这个具体实现就是后期具体设计的了。所以先设计一个接口来统筹行为方案,怎么去实现就有很大的扩展性。都是打印文件,只不过采取的方式不同,从这方面来看接口的不同实现有殊途同归的性质:都是为了实现一个具体的行为。就比如不同的打印实现都是为了打印文件来服务的。

这在里Poster接口也是做了类似于PrintFile接口的事儿,在EventBus里面Poster的实现类:

其中AsyncPoster处理的就是ThreadModel.ASYNC,HandlerPoster对应ThreadModel.MAIN和ThreadModel.MAIN_ORDER,至于BackgroundPoster则是处理的ThreadModel.BACKGROUND的了,也是本篇博文的主角。


对象用来封装数据和行为,而对象之间往往是需要不断传递的。下面讲一下EventBus 中一个重要的类PendingPost,该类的结构也很简单:

class PendingPost {private final static List<PendingPost> pendingPostPool = new ArrayList<PendingPost>();//具体的事件对象Object event;Subscription subscription;//next指针PendingPost next;}

可以看出PendingPost除了封装了事件对象event,事件接收者对象Subscription之外,还弄了个next引用,这明白着就是构建一个链表结构,注意该类还有个静态的pendingPostPool集合,该集合包含了PendingPost对象,至于这个集合的作用后面再讲。首先所以我们来看看EventBus是怎么构建这个PendingPost链表的,具体的构建过程在PendingPostQueue类里面,先瞧瞧这个类有啥:

 class PendingPostQueue {//链表头部引用private PendingPost head;//链表尾部指针private PendingPost tail;}

注意该类算是EventBus的核心类之一,在AsyncPoster,BackgroundPost,HandlerPoster里都有用到,该类的强大作用就是构建具有head和tail引用的PendingPost链表结构。具体的构建行为在PendingPostQueue对象的enqueue方法里面:


synchronized void enqueue(PendingPost pendingPost) {if (tail != null) {tail.next = pendingPost;tail = pendingPost;} else if (head == null) {//第一次执行是走这个分支head = tail = pendingPost;} //唤醒等待的线程notifyAll();}

上面的方法第一次执行的时候形成了下面的结果:

多次调用的话,很明显形成的链表结构如下:


其实也就是一个简单的链表结构,也就是说enqueue方法的作用就是将EventBus post的事件形成一个如上图所示的链表。那么我们怎么获取链表中的对象呢,对应的PendingPostQueue有了poll方法:

   synchronized PendingPost poll() {//返回head节点,并将head指针下移PendingPost pendingPost = head;if (head != null) {head = head.next;if (head == null) {tail = null;}}return pendingPost;}synchronized PendingPost poll(int maxMillisToWait) throws InterruptedException {//列表为空,等待并释放锁if (head == null) {wait(maxMillisToWait);}return poll();}

所以经过poll方法处理一次后,上图中的head引用会指向PendingPost2节点。一句话总结PendingPostQueue的作用就是构建上图链表和从列表中获取PendingPost的过程,且在构建的过程中如果构建完毕则调用notifyAll,取PendingPost的时候如果链表为null则wait ,可以说是一个简单的生产者消费者模式,其实这也是借用了享元模式的思想,减少对象的创建节约内存。


BackgroundPoster简单分析
上面提到PendingPostQueue在EventBus Poster接口的三个实现类中都有使用,所以看看先BackgroundPoster这个类的具体实现:

//注意BackgroundPoster是一个Runnable
class BackgroundPoster implements Runnable, Poster {//事件链表private final PendingPostQueue queue;private final EventBus eventBus;//volatile确保可见性,但是不能保证原子性//判断事件是否得到执行private volatile boolean executorRunning;BackgroundPoster(EventBus eventBus) {this.eventBus = eventBus;queue = new PendingPostQueue();}}

BackgroundPoster是一个实现了Poster的Runnable, 且看poster的enqueue在BackgroundPoster的具体实现:

 @Overridepublic void enqueue(Subscription subscription, Object event) {//1、获取一个pendingPostPendingPost pendingPost = PendingPost.obtainPendingPost(subscription, event);synchronized (this) {//2、构建事件链表queue.enqueue(pendingPost);//3、执行事件if (!executorRunning) {executorRunning = true;eventBus.getExecutorService().execute(this);}}}

正如代码注释所示,上述方法所了三件事:
1、根据事件event和事件接收者subscription获取一个PendingPost对象,
2、讲获取到PendingPost对象交给queue.enqueue构成上图的链表结构
3、调用eventBus.getExecutorService来处理事件,会调用BackgroundPoster的fun方法。

下面详细说一下事件1:具体的是PendingPost的静态方法obtainPendingPost来构建一个PendingPost:

 //PendingPost集合final static List<PendingPost> pendingPostPool=new ArrayList<PendingPost>();static PendingPost obtainPendingPost(Subscription subscription, Object event) {//从pendingPostPool获取一个PendingPostsynchronized (pendingPostPool) {int size = pendingPostPool.size();if (size > 0) {//从集合末尾获取一个PendingPost pendingPost = pendingPostPool.remove(size-1);pendingPost.event = event;pendingPost.subscription = subscription;pendingPost.next = null;return pendingPost;}}//如果pendingPostPool没有,则创建一个return new PendingPost(event, subscription);}

逻辑很清晰,就是先从List集合的尾部中调用remove获取一个PendingPost对象,注意List集合中的PendingPost对象所封装的数据都为空,也就是:

        pendingPost.event == null;pendingPost.subscription == null;pendingPost.next == null;

然后将obtainPendingPost方法里的参数赋值给该pendingPost对象并返回。如果list集合为空,则new 一个PendingPost返回,注意此时新创建的PendingPost的数据不为空,并没有调用list.add方法将对象放入进去。那么什么时候将PendingPost对象add进来呢?当然是事件处理完毕后,此时调用PendingPost的releasePendingPost静态方法:

   //处理完事件后将pendingPost数据指控,可比喻为空瓶回收static void releasePendingPost(PendingPost pendingPost) {//数据置空pendingPost.event = null;pendingPost.subscription = null;pendingPost.next = null;//数据指控后list回收,感觉跟空瓶回收一样//水喝完了,瓶子单独回收synchronized (pendingPostPool) {// Don't let the pool grow indefinitelyif (pendingPostPool.size() < 10000) {pendingPostPool.add(pendingPost);}}}

所以list集合的作用就是有点类似空瓶回收的作用,在这里PendingPost就是瓶子,瓶子里的水(事件)喝完后,将瓶子回收过来等下次再装水(事件),好处PendingPost这个对象得到了复用,必须了对象的频繁创建

说完了PendingPost的创建和回收,以及PendingPost链表的构建是时候看看事件的消费了,当然是在BackgroundPoster的run方法中对事件进行分发处理,所以看看run方法都怎么处理的:

 public void run() {try {try {//循环获取链表,并处理直到链表为nullwhile (true) {//1、从连表中获取一个PendingPost,链表为null则等待一秒PendingPost pendingPost = queue.poll(1000);if (pendingPost == null) {synchronized (this) {// Check again, this time in synchronized//继续获取pendingPostpendingPost = queue.poll();//如果还为null,认定此时没有事件产生if (pendingPost == null) {executorRunning = false;//退出run方法return;}}}//在这里会调用PendingPost.releasePendingPosteventBus.invokeSubscriber(pendingPost);}} catch (InterruptedException e) {eventBus.getLogger().log(Level.WARNING, Thread.currentThread().getName() + " was interruppted", e);}} finally {executorRunning = false;}}

可以看出run总体上来说就是循环PendingPost链表,然后调用eventBus.invokeSubscriber(pendingPost);该方法主要是将event交给@subsribe方法进行invoke调用。需要注意的是invokeSubscriber方法里面调用了PendingPost.releasePendingPost,该方法上面说过就是对pendingPost进行回收再利用(见上图)。

    void invokeSubscriber(PendingPost pendingPost) {Object event = pendingPost.event;Subscription subscription = pendingPost.subscription;//回收pendingPost对象,以后复用PendingPost.releasePendingPost(pendingPost);if (subscription.active) {//真正执行method.invoke的地方invokeSubscriber(subscription, event);}}//真正执行method.invoke的地方void invokeSubscriber(Subscription subscription, Object event) {try {subscription.subscriberMethod.method.invoke(subscription.subscriber, event);} catch (InvocationTargetException e) {handleSubscriberException(subscription, event, e.getCause());} catch (IllegalAccessException e) {throw new IllegalStateException("Unexpected exception", e);}}

需要额外注意的是,在while循环中,先调用poll(1000)方法,如果事件链表为null,则wait(1000),等待事件产生;而后再次在用poll()获取,如果此时仍然为null,则EventBus认为现阶段没有事件,则run方法执行结束,直到下次事件来临后继续调用eventBus.getExecutorService().execute(this)执行。

还需要注意的是因为在fun方法中循环便利事件链表,所以为了防止事件阻塞,在使用ThreadModel.BACKGROUND的时候,要求此@Subricebe 方法不要执行耗时的逻辑。比如网络请求等。就像官方注释所说:

 EventBus uses a single background thread, that will deliver all its events sequentially. Subscribers using this mode should try to  return quickly to avoid blocking the background thread

那么耗时的逻辑在哪儿执行呢?耗时的逻辑用ThreadModel.ASYNC 模式,因为该模式很简单就是获取链表中的一个事件,执行之;而不像BackGround那样循环整个事件链表挨个执行之。

  //AsyncPoster的run方法public void run() {PendingPost pendingPost = queue.poll();if(pendingPost == null) {throw new IllegalStateException("No pending post available");}eventBus.invokeSubscriber(pendingPost);}

到此为止BackgrounPoster讲解完毕,如有不当之处欢迎批评指正,共同学习

EventBus BackgroundPoster原理解析相关推荐

  1. java eventbus 原理_本文为 Android 开源项目实现原理解析 EventBus 部分,从源码分析 EventBus 的实现原理...

    之前太忙导致 Android 开源项目实现原理解析 一度搁浅,目前一期进行中,我也完成了 EventBus 分析的初稿,大家可以稍微看看后面会继续润色下. PS:本文直接复制 Markdown,格式有 ...

  2. 事件总线框架EventBus的使用与原理解析

    文章目录 1. EventBus框架 1.1 EventBus简介 1.2 EventBus基本使用 1.2.1 添加Gradle依赖 1.2.2 定义事件 1.2.3 准备订阅者 1.2.4 发布事 ...

  3. EventBus源码解析

    前面一篇文章讲解了EventBus的使用,但是作为开发人员,不能只停留在仅仅会用的层面上,我们还需要弄清楚它的内部实现原理.所以本篇博文将分析EventBus的源码,看看究竟它是如何实现"发 ...

  4. Spark Shuffle原理解析

    Spark Shuffle原理解析 一:到底什么是Shuffle? Shuffle中文翻译为"洗牌",需要Shuffle的关键性原因是某种具有共同特征的数据需要最终汇聚到一个计算节 ...

  5. 秋色园QBlog技术原理解析:性能优化篇:用户和文章计数器方案(十七)

    2019独角兽企业重金招聘Python工程师标准>>> 上节概要: 上节 秋色园QBlog技术原理解析:性能优化篇:access的并发极限及分库分散并发方案(十六)  中, 介绍了 ...

  6. Tomcat 架构原理解析到架构设计借鉴

    ‍ 点击上方"方志朋",选择"设为星标" 回复"666"获取新整理的面试文章 Tomcat 架构原理解析到架构设计借鉴 Tomcat 发展这 ...

  7. 秋色园QBlog技术原理解析:性能优化篇:数据库文章表分表及分库减压方案(十五)...

    文章回顾: 1: 秋色园QBlog技术原理解析:开篇:整体认识(一) --介绍整体文件夹和文件的作用 2: 秋色园QBlog技术原理解析:认识整站处理流程(二) --介绍秋色园业务处理流程 3: 秋色 ...

  8. CSS实现元素居中原理解析

    原文:CSS实现元素居中原理解析 在 CSS 中要设置元素水平垂直居中是一个非常常见的需求了.但就是这样一个从理论上来看似乎实现起来极其简单的,在实践中,它往往难住了很多人. 让元素水平居中相对比较简 ...

  9. 秋色园QBlog技术原理解析:Web之页面处理-内容填充(八)

    文章回顾: 1: 秋色园QBlog技术原理解析:开篇:整体认识(一) --介绍整体文件夹和文件的作用 2: 秋色园QBlog技术原理解析:认识整站处理流程(二) --介绍秋色园业务处理流程 3: 秋色 ...

  10. 秋色园QBlog技术原理解析:UrlRewrite之无后缀URL原理(三)

    文章回顾: 1: 秋色园QBlog技术原理解析:开篇:整体认识(一) --介绍整体文件夹和文件的作用 2: 秋色园QBlog技术原理解析:认识整站处理流程(二) --介绍秋色园业务处理流程 本节,将从 ...

最新文章

  1. web架构设计经验分享
  2. LeetCode--495
  3. TabHost 两种使用方法 直接让一个Activity 继承TabActivity 和 利用findViwById()方法取得TagHost组件...
  4. IntelliJ IDEA自动部署
  5. Java中的Runnable、Callable、Future、FutureTask
  6. 五个人+三个月=美摄云非编1.0 | 我们采访到了“工期很紧“的美摄研发总监
  7. 搞怪的html代码,这个恶搞网页代码是肿么写出来的?
  8. 数据结构与算法——二叉排序树详解以及代码实现
  9. 致SOA架构师:注意瀑布式开发方法
  10. 【读书笔记】—— 西方人文社科经典
  11. uc浏览器设置里面的的浏览器ua是什么意思
  12. 高品质摄影作图台式计算机推荐,能拍出高品质作品的强大系统 摄影师段岳衡专访...
  13. KubeEdge+Fabedge集成环境搭建教程
  14. Gamma Correction(伽马校正)
  15. 【读书笔记】《中庸(国学大书院)》
  16. 工作室课题—贪吃的大嘴(蓝桥杯)
  17. 10月27号吃鸡服务器维护吗,10月27日正式服维护公告
  18. 20140426组队赛总结
  19. 诺奖终属黑洞理论-IT与物理的相互成就
  20. 中国象棋软件-引擎实现(二)棋局表示

热门文章

  1. 大数据学情分析_大数据时代|如何轻松做好学情分析
  2. java过滤器执行按什么排序_servlet过滤器及监听器
  3. Cesium:实现动态画点、线并测距
  4. mysql dba 试题_MySQLDBA面试题-上海热璞科技
  5. datatable的查询介绍
  6. CVPR2021中的目标检测和语义分割论文汇总
  7. DEV 实现CheckBox单选
  8. Struts2(三)
  9. slz-servlet的引入
  10. js调用百度地图搜索功能