前言

  在前面,我们探讨了如何在自己的代码中引入EventBus,进行基本的事件分发/监听;对注册观察者与事件发送的过程进行了浅析。从之前的学习中,我们了解到,EventBus一共有4种onEvent方法,要根据实际需求的不同选用不同的事件处理方法。本篇blog中,我们集中研究一下这四种事件处理方法内部分别做了什么事情,是如何实现的。

  本篇会较多涉及java中的并发/多线程技术,这部分基础不够扎实的朋友,可以趁机黑练一下。(其实是说我自己 -_-!)

post的最后一步

  EventBus.post 是分发事件时调用的方法,post时,根据EventType找到对应的Subscription列表,遍历列表依次调用postToSubscription。方法如下

    private void postToSubscription(Subscription subscription, Object event, boolean isMainThread) {
switch (subscription.subscriberMethod.threadMode) {
case PostThread:
invokeSubscriber(subscription, event);
break;
case MainThread:
if (isMainThread) {
invokeSubscriber(subscription, event);
} else {
mainThreadPoster.enqueue(subscription, event);
}
break;
case BackgroundThread:
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);
}
}

  根据EventBus提供的4种消息处理方式(ThreadMode)进行switch/case,这4种ThreadMode的含义,在之前的文章中已经给出过说明。结合代码,能看出在这四个case语句里面,处理方式无非以下两种:

  1. 若当前线程是ThreadMode所指明的线程,直接调用 invokeSubscriber(subscription, event);
  2. 当前线程不是ThreadMode所指明的线程,将 subscription、event 加入到目标线程的队列中(enqueue)。

  接下来逐一分析这两种处理方式。

交由当前线程处理

  从方法名与方法所起的作用来看,invokeSubscriber一定是用到了反射机制,代码中是如何做的呢?

    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);
}
}

  如此简单,subscription中保存了之前解析出的以“onEvent”开头的方法,直接调用之。当然要catch住invoke可能会抛出的三个Exception:IllegalArgumentException, InvocationTargetException, IllegalAccessException,不知为何这里只catch了其中两个。我判断原因是,之前解析subscription时,已经就参数类型做过过滤了,可以保证传入的event一定是方法所需要的,所以就无需再去catch参数类型不匹配的Exception。

交由非当前线程处理

  在EventBus中,声明了如下三个Poster

    private final HandlerPoster mainThreadPoster;
private final BackgroundPoster backgroundPoster;
private final AsyncPoster asyncPoster;

  这三个Poster里,都有enqueue方法,用来将事件压入队列。但是这三种处理方式是略有不同的,依次来看。

HandlerPoster

final class HandlerPoster extends Handler {

    private final PendingPostQueue queue;
private final int maxMillisInsideHandleMessage;
private final EventBus eventBus;
private boolean handlerActive; HandlerPoster(EventBus eventBus, Looper looper, int maxMillisInsideHandleMessage) {
super(looper);
this.eventBus = eventBus;
this.maxMillisInsideHandleMessage = maxMillisInsideHandleMessage;
queue = new PendingPostQueue();
} void enqueue(Subscription subscription, Object event) {
PendingPost pendingPost = PendingPost.obtainPendingPost(subscription, event);
synchronized (this) {
queue.enqueue(pendingPost);
if (!handlerActive) {
handlerActive = true;
if (!sendMessage(obtainMessage())) {
throw new EventBusException("Could not send handler message");
}
}
}
} @Override
public void handleMessage(Message msg) {
boolean rescheduled = false;
try {
long started = SystemClock.uptimeMillis();
while (true) {
PendingPost pendingPost = queue.poll();
if (pendingPost == null) {
synchronized (this) {
// Check again, this time in synchronized
pendingPost = queue.poll();
if (pendingPost == null) {
handlerActive = false;
return;
}
}
}
eventBus.invokeSubscriber(pendingPost);
long timeInMethod = SystemClock.uptimeMillis() - started;
if (timeInMethod >= maxMillisInsideHandleMessage) {
if (!sendMessage(obtainMessage())) {
throw new EventBusException("Could not send handler message");
}
rescheduled = true;
return;
}
}
} finally {
handlerActive = rescheduled;
}
}
}

  HandlerPoster,人如其名,继承了Handler类,内部维护了一个PendingPostQueue,每当有时间enqueue时,判断当前Poster是否处于激活状态。对于未激活的将其激活。激活中的Poster,会通过sendMessage(obtainMessage())来依次处理PendingPostQueue中的事件,这是通过重载Handler中的handleMessage来做的。

BackgroundPoster

final class BackgroundPoster implements Runnable {

    private final PendingPostQueue queue;
private final EventBus eventBus; private volatile boolean executorRunning; BackgroundPoster(EventBus eventBus) {
this.eventBus = eventBus;
queue = new PendingPostQueue();
} public void enqueue(Subscription subscription, Object event) {
PendingPost pendingPost = PendingPost.obtainPendingPost(subscription, event);
synchronized (this) {
queue.enqueue(pendingPost);
if (!executorRunning) {
executorRunning = true;
eventBus.getExecutorService().execute(this);
}
}
} @Override
public void run() {
try {
try {
while (true) {
PendingPost pendingPost = queue.poll(1000);
if (pendingPost == null) {
synchronized (this) {
// Check again, this time in synchronized
pendingPost = queue.poll();
if (pendingPost == null) {
executorRunning = false;
return;
}
}
}
eventBus.invokeSubscriber(pendingPost);
}
} catch (InterruptedException e) {
Log.w("Event", Thread.currentThread().getName() + " was interruppted", e);
}
} finally {
executorRunning = false;
}
} }

  与HandlerPoster不同的是,BackgroundPoster并没有继承自Handler,而是实现了Runnable接口。这么做的原因是,在用到BackgroudPoster的场合,必须是新建一个进程来处理事件,这里就使用了eventBus.getExecutorService().execute(this),从线程池里取出线程来执行。可以看到后面的AsyncPoster也是采用类似的方法。

AsyncPoster

class AsyncPoster implements Runnable {

    private final PendingPostQueue queue;
private final EventBus eventBus; AsyncPoster(EventBus eventBus) {
this.eventBus = eventBus;
queue = new PendingPostQueue();
} public void enqueue(Subscription subscription, Object event) {
PendingPost pendingPost = PendingPost.obtainPendingPost(subscription, event);
queue.enqueue(pendingPost);
eventBus.getExecutorService().execute(this);
} @Override
public void run() {
PendingPost pendingPost = queue.poll();
if(pendingPost == null) {
throw new IllegalStateException("No pending post available");
}
eventBus.invokeSubscriber(pendingPost);
}
}

  同样是用Runnable来实现,为啥AsyncPoster的代码比BackgroundPoster的简单这么多呢?仔细对比一下,就会发现BackgroudPoster中多出的代码,都是用来处理同步的。所以,原因在于,BackgroudPoster必须保证事件处理的顺序,先进先出。而AsyncQueue则没有这个顾虑。所以在需要按顺序处理事件的场合,就不要使用AsyncPoster啦!

小结

  本篇blog简单介绍了四种ThreadMode内部的机制,如果需要执行事件的线程是当前线程,则直接用反射调用方法;否则将事件压入对应Poster的队列中,依次(HandlerPoster,BackgroundPoster)或异步(AsyncPoster)执行。

下期预告

  从目录结构上,对EventBus项目进行整体解析。

[EventBus源码解析] 订阅者处理消息的四种ThreadMode的更多相关文章

  1. EventBus源码解析 源码阅读记录

    EventBus源码阅读记录 repo地址: greenrobot/EventBus EventBus的构造 双重加锁的单例. static volatile EventBus defaultInst ...

  2. 【Android】EventBus 源码解析

    EventBus 源码解析 本文为 Android 开源项目实现原理解析 中 EventBus 部分项目地址:EventBus,分析的版本:ccc2771,Demo 地址:EventBus Demo分 ...

  3. Android EventBus源码解析 带你深入理解EventBus

    转载请标明出处:http://blog.csdn.net/lmj623565791/article/details/40920453,本文出自:[张鸿洋的博客] 上一篇带大家初步了解了EventBus ...

  4. 时序数据库 Apache-IoTDB 源码解析之文件数据块(四)

    上一章聊到行式存储.列式存储的基本概念,并介绍了 TsFile 是如何存储数据以及基本概念.详情请见: 时序数据库 Apache-IoTDB 源码解析之文件格式简介(三) 打一波广告,欢迎大家访问Io ...

  5. springboot源码解析-管中窥豹系列之Initializer(四)

    一.前言 Springboot源码解析是一件大工程,逐行逐句的去研究代码,会很枯燥,也不容易坚持下去. 我们不追求大而全,而是试着每次去研究一个小知识点,最终聚沙成塔,这就是我们的springboot ...

  6. EventBus源码解析

    用例 本文主要按照如下例子展开: //1. 新建bus对象,默认仅能在主线程上对消息进行调度 Bus bus = new Bus(); // maybe singleton //2. 新建类A(sub ...

  7. Android EventBus源码解析

    项目地址 :https://github.com/greenrobot/EventBus 这个项目个人感觉就是为了解决回调事件过多的,比方说A函数在做完以后 要调用b类的c函数,那我们通常的做法就是 ...

  8. 76.Android之EventBus源码解析

    转载:http://p.codekk.com/blogs/detail/54cfab086c4761e5001b2538 1. 功能介绍 1.1 EventBus EventBus 是一个 Andro ...

  9. [EventBus源码解析] EventBus.post 方法详述

    前情概要 上一篇blog我们了解了EventBus中register/unregister的过程,对EventBus如何实现观察者模式有了基本的认识.今天我们来看一下它是如何分发一个特定事件的,即po ...

随机推荐

  1. JDK1.5新特性

    静态导入 import static java.util.Collections.*; import static java.lang.System.out; 1.如果静态导入的成员与本类的成员存在同 ...

  2. bzoj1211: prufer序列 | [HNOI2004]树的计数

    题目大意: 告诉你树上每个节点的度数,让你构建出这样一棵树,问能够构建出树的种树 这里注意数量为0的情况,就是 当 n=1时,节点度数>0 n>1时,所有节点度数相加-n!=n-2 可以通 ...

  3. HttpURLConnection网络请求

    //创建访问的方法 public String Check_json(){ //创建一个结果字符串 String result=""; //拼接字符串 StringBuffer s ...

  4. java eclipse环境搭建环境

    开发环境搭建: JDK的安装 http://www.oracle.com/technetwork/java/javase/downloads 下载文件:jdk-8u101-windows-x64.ex ...

  5. HBase with MapReduce (Summary)

    我们知道,hbase没有像关系型的数据库拥有强大的查询功能和统计功能,本文实现了如何利用mapreduce来统计hbase中单元值出现的个数,并将结果携带目标的表中, (1)mapper的实现 pac ...

  6. 进监狱全攻略之 Mifare1 Card 破解

    补充新闻:程序员黑餐馆系统 给自己饭卡里充钱 ,技术是双刃剑,小心,小心! 前言 从M1卡的验证漏洞被发现到现今,破解设备层出不穷,所以快速傻瓜式一键破解不是本文的重点,年轻司机将从本文中获得如下技能 ...

  7. 在线工具、setHtmlRem、px2rem

    http://tool.lu/c/developer  开发类在线工具 https://github.com/leon776/setHtmlRem   setHtmlRem https://githu ...

  8. 导入别人的flex项目出现的问题

    1.unable to open 'D:/flex-projects/RoadService/WebContent/WEB-INF/flex/services-config.xml' 这种情况是因为别 ...

  9. webpack脚手架搭建(简单版)

    运行命令 安装依赖:npm install 运行项目:npm start 大致流程 npm init:新建 package.json 将需要的依赖模块加入 dependencies(生产环境) 和 d ...

  10. Java-->Gson序列化及反序列化

    --> 首先导入jar包,并添加到Build Path --> 需要User类:有属性.构造方法和setter.getter方法. --> Test 测试类: package com ...