post()方法调用流程

我们继续来看EventBus类,的另一个入口方法post()

  1. //已省略部分代码
  2. public void post(Object event) {
  3. PostingThreadState postingState = currentPostingThreadState.get();
  4. List<Object> eventQueue = postingState.eventQueue;
  5. eventQueue.add(event);
  6. if (!postingState.isPosting) {
  7. postingState.isMainThread = Looper.getMainLooper() == Looper.myLooper();
  8. postingState.isPosting = true;
  9. if (postingState.canceled) {
  10. throw new EventBusException("Internal error. Abort state was not reset");
  11. }
  12. while (!eventQueue.isEmpty()) {
  13. postSingleEvent(eventQueue.remove(0), postingState);
  14. }
  15. postingState.isPosting = false;
  16. postingState.isMainThread = false;
  17. }
  18. }

post() 方法首先从 currentPostingThreadState 对象中取了一个 PostingThreadState ,我们来看看这个
currentPostingThreadState 对象的创建代码。

  1. private final ThreadLocal<PostingThreadState> currentPostingThreadState = new
  2. ThreadLocal<PostingThreadState>() {
  3. @Override
  4. protected PostingThreadState initialValue() {
  5. return new PostingThreadState();
  6. }
  7. };

ThreadLocal 是一个线程内部的数据存储类,通过它可以在指定的线程中存储数据,而这段数据是不会与其他线程共享的。其内部原理是通过生成一个它包裹的泛型对象的数组,在不同的线程会有不同的数组索引值,通过这样就可以做到每个线程通过
get() 方法获取的时候,取到的只能是自己线程所对应的数据。 

在 EventBus 中, ThreadLocal 所包裹的是一个 PostingThreadState 类,它仅仅是封装了一些事件发送中过程所需的数据。

  1. final static class PostingThreadState {
  2. //通过post方法参数传入的事件集合
  3. final List<Object> eventQueue = new ArrayList<Object>();
  4. boolean isPosting; //是否正在执行postSingleEvent()方法
  5. boolean isMainThread;
  6. Subscription subscription;
  7. Object event;
  8. boolean canceled;
  9. }

回到 post() 方法,我们看到其核心代码是这句:

  1. while (!eventQueue.isEmpty()) {
  2. postSingleEvent(eventQueue.remove(0), postingState);
  3. }

每次调用post()的时候都会传入一个事件,这个事件会被加入到队列。而每次执行postSingleEvent()都会从队列中取出一个事件,这样不停循环取出事件处理,直到队列全部取完。 

再看 postSingleEvent() 方法

  1. private void postSingleEvent(Object event, PostingThreadState postingState) throws Error {
  2. Class<?> eventClass = event.getClass();
  3. boolean subscriptionFound = false;
  4. if (eventInheritance) {
  5. //获取到eventClass所有父类的集合
  6. List<Class<?>> eventTypes = lookupAllEventTypes(eventClass);
  7. int countTypes = eventTypes.size();
  8. for (int h = 0; h < countTypes; h++) {
  9. Class<?> clazz = eventTypes.get(h);
  10. //左或右只要有一个为真则为真,并赋值给左
  11. subscriptionFound |= postSingleEventForEventType(event, postingState, clazz);
  12. }
  13. } else {
  14. subscriptionFound = postSingleEventForEventType(event, postingState, eventClass);
  15. }
  16. if (!subscriptionFound) {
  17. if (logNoSubscriberMessages) {
  18. Log.d(TAG, "No subscribers registered for event " + eventClass);
  19. }
  20. //参考sendNoSubscriberEvent注释
  21. if (sendNoSubscriberEvent && eventClass != NoSubscriberEvent.class &&
  22. eventClass != SubscriberExceptionEvent.class) {
  23. post(new NoSubscriberEvent(this, event));
  24. }
  25. }
  26. }

还记得 EventBusBuild 中的 eventInheritance是做什么的吗?它表示一个子类事件能否响应父类的onEvent() 方法。

再往下看 lookupAllEventTypes() 它通过循环和递归一起用,将一个类的父类,接口,父类的接口,父类接口的父类,全部添加到全局静态变量 eventTypes 集合中。之所以用全局静态变量的好处在于用全局静态变量只需要将那耗时又复杂的循环+递归方法执行一次就够了,下次只需要通过
key:事件类名 来判断这个事件是否以及执行过 lookupAllEventTypes() 方法。

postSingleEventForEventType()方法

然后我们继续往下,看发送方法 postSingleEventForEventType()

  1. private boolean postSingleEventForEventType(Object event, PostingThreadState postingState, Class<?> eventClass) {
  2. CopyOnWriteArrayList<Subscription> subscriptions;
  3. synchronized (this) {
  4. //所有订阅了eventClass的事件集合
  5. subscriptions = subscriptionsByEventType.get(eventClass);
  6. }
  7. if (subscriptions != null && !subscriptions.isEmpty()) {
  8. //回调subscription的响应方法
  9. for (Subscription subscription : subscriptions) {
  10. postingState.event = event;
  11. postingState.subscription = subscription;
  12. boolean aborted = false;
  13. try {
  14. postToSubscription(subscription, event, postingState.isMainThread);
  15. aborted = postingState.canceled;
  16. } finally {
  17. postingState.event = null;
  18. postingState.subscription = null;
  19. postingState.canceled = false;
  20. }
  21. if (aborted) {
  22. break;
  23. }
  24. }
  25. return true;
  26. }
  27. return false;
  28. }

它首先通过这一句

  1. subscriptions = subscriptionsByEventType.get(eventClass);

获取到所有订阅了 eventClass 的事件集合,之前有讲过, subscriptionsByEventType 是一个以 key:订阅的事件 value:订阅这个事件的所有订阅者集合 的 Map 。

最后通过循环,遍历所有订阅了 eventClass 事件的订阅者,并向每一个订阅者发送事件。

看它的发送事件的方法:

postToSubscription(subscription, event, postingState.isMainThread); 

噢,又回到了和之前 Subscribe 流程中处理粘滞事件相同的方法里————对声明不同线程模式的事件做不同的响应方法,最终都是通过invokeSubscriber()反射订阅者类中的以onEvent开头的方法。

unregister()

我们继续来看EventBus类,的最后一个入口方法unregister()

  1. public synchronized void unregister(Object subscriber) {
  2. List<Class<?>> subscribedTypes = typesBySubscriber.get(subscriber);
  3. if (subscribedTypes != null) {
  4. for (Class<?> eventType : subscribedTypes) {
  5. //取消注册subscriber对eventType事件的响应
  6. unsubscribeByEventType(subscriber, eventType);
  7. }
  8. //当subscriber对所有事件都不响应以后,移除订阅者
  9. typesBySubscriber.remove(subscriber);
  10. }
  11. }

之前讲过typesBySubscriber key:订阅者对象 value:这个订阅者订阅的事件集合,表示当前订阅者订阅了哪些事件。 

首先遍历要取消注册的订阅者订阅的每一个事件,调用unsubscribeByEventType(),从这个事件的所有订阅者集合中将要取消注册的订阅者移除。最后再以:当前订阅者为
key 全部订阅事件集合为 value 的一个 Map 的 Entry 移除,就完成了取消注册的全部过程。

EventBus工作原理

最后我们再来从设计者的角度看一看EventBus的工作原理。

订阅的逻辑

1、首先是调用register()方法注册一个订阅者A。

2、遍历这个订阅者A的全部以onEvent开头的订阅方法。

3、将A订阅的所有事件分别作为 key,所有能响应 key 事件的订阅者的集合作为 value,存入 Map<事件,List<订阅这个事件的订阅者>>

4、以A的类名为 key,所有 onEvent 参数类型的类名组成的集合为 value,存入 Map<订阅者,List<订阅的事件>>。

4.1、如果是订阅了粘滞事件的订阅者,从粘滞事件缓存区获取之前发送过的粘滞事件,响应这些粘滞事件。

发送事件的逻辑

1、取当前线程的发送事件封装数据,并从封装的数据中拿到发送事件的事件队列。

2、将要发送的事件加入到事件队列中去。

3、循环,每次发送队列中的一条事件给所有订阅了这个事件的订阅者。

3.1、如果是子事件可以响应父事件的事件模式,需要先将这个事件的所有父类、接口、父类的接口、父类接口的父类都找到,并让订阅了这些父类信息的订阅者也都响应这条事件。

响应事件的逻辑

1、发送事件处理完成后会将事件交给负责响应的逻辑部分。

2、首先判断时间的响应模式,响应模式分为四种:

PostThread 在哪个线程调用的post()方法,就在哪个线程执行响应方法。

MainThread 无论是在哪个线程调用的post()方法,最终都在主线程执行响应方法。

BackgroundThread 无论是在哪个线程调用的post()方法,最终都在后台线程执行响应方法。(串行执行,一次只执行一个任务,其他任务在队列中处于等待状态)

Async 无论是在哪个线程调用的post()方法,最终都在后台线程执行响应方法。(并行执行,只要有任务就开一个线程让他执行)

取消注册的逻辑

1、首先是调用unregister()方法拿到要取消注册的订阅者B。 

2、从这个类订阅的时候存入的 Map<订阅者,List<订阅的事件>> 中,拿到这个类的订阅事件集合。

3、遍历订阅时间集合,在注册的时候存入的 Map<事件,List<订阅这个事件的订阅者>> 中将对应订阅事件的订阅者集合中的这个订阅者移除。

4、将步骤2中的 Map<订阅者,List<订阅的事件>> 中这个订阅者相关的 Entry 移除。

工作原理图示

android EventBus详解(三)的更多相关文章

  1. android EventBus详解(一)

    EventBus 是一款针对Android优化的发布/订阅事件总线.主要功能是替代Intent, Handler, BroadCast 在 Fragment,Activity,Service,线程之间 ...

  2. (转)android Fragments详解三:实现Fragment的界面

    为fragment添加用户界面 fragment一般作为activity的用户界面的一部分,把它自己的layout嵌入到activity的layout中.    一个 要为fragment提供layo ...

  3. 【转】Android编译系统详解(三)——编译流程详解

    原文网址:http://www.cloudchou.com/android/post-276.html 本文原创作者:Cloud Chou. 欢迎转载,请注明出处和本文链接 1.概述 编译Androi ...

  4. Android Fragment详解(三): 实现Fragment的界面

    为fragment添加用户界面: Fragment一般作为activity的用户界面的一部分,把它自己的layout嵌入到activity的layout中. 一个 要为fragment提供layout ...

  5. Android ActionBar详解(三)--->ActionBar的Home导航功能

    FirstActivity如下: package cc.testsimpleactionbar2; import android.os.Bundle; import android.app.Activ ...

  6. android EventBus详解(二)

    上一节讲了EventBus的使用方法和实现的原理,下面说一下EventBus的Poster只对粘滞事件和invokeSubscriber()方法是怎么发送的. Subscribe流程 我们继续来看Ev ...

  7. Android ActionBar详解(三):ActionBar实现切换Tabs标签

    实现切换Tabs标签; Activity代码: public class ActionBarTabs extends Activity { @Override protected void onCre ...

  8. Android 布局详解 -三表格布局(TableLayout)以及重要属性

              TableLayout跟TableRow 是一组搭配应用的布局,TableLayout置底,TableRow在TableLayout的上方,而Button.TextView等控件就 ...

  9. Android Loader详解三:重启与回调

    重启装载器 当你使用initLoader()时,如果指定ID的装载器已经存在,则它使用这个装载器.如果不存在呢,它将创建一个新的.但是有时你却是想丢弃旧的然后开始新的数据. 要想丢弃旧数据,你应使用r ...

随机推荐

  1. Hibernate之配置文件

    可持久化对象有以下三种状态: 临时状态(Transient):对象在保存进数据库之前为临时状态,这时数据库中没有该对象的信息,如果没有持久化,程序退出后临时状态的对象信息将会丢失.随时可能被垃圾回收器 ...

  2. 16 Content Provider总结

    第16天 Content Provider 一, 什么是Content Provider? 内容提供者 Android四大主件之一 :短信记录 通讯录 联系人 自定义 >Content Prov ...

  3. UI设计--->全心全意为人民服务的宗旨---->注重客户体验--->软件持久的生命力

    UI即User Interface(用户界面)的简称.UI设计是指对软件的人机交互.操作逻辑.界面美观的整体设计.好的UI设计不仅是让软件变得有个性有品味,还要让软件的操作变得舒适简单.自由,充分体现 ...

  4. Java基础---Java---IO流-----BufferedReader、BufferedWriter、缓冲区、装饰设计模式及和继承的区别

    IO流 IO流用来处理设备之间的数据传输 java对数据的操作是过流的方式 流按操作数据分为两种:字节流与字符流 流按流向分为:输入流,输出流. IO流常用基类 字节流的抽象基类:InputStrea ...

  5. scala学习笔记3(trait)

    // trait 类似于 Java8 中可以带 default method 的接口. // trait 中可以带有实现的方法,也可以带有抽象的方法,使用 trait 的方式是 with 而混入类中 ...

  6. Dynamics CRM2015 2015版本可用的OData Query Designer工具

    2015后很多工具无法使用,包括2011版的OData Query Designer,这里介绍一款可用的工具,Dynamics XRM Tools for CRM 2015,下载地址:https:// ...

  7. 错误 frm-40654 记录已经被另一个用户更新,重新查询以查看修改

     导致这问题的原因有多个,有些是最近在项目上发现不同于网上其他人遇到的 网上一般来说大家都说有如下几个原因.但是在项目上做返利时 对AP invoice 的有做更改,导致更改或插入的数据在界面上修 ...

  8. 【Shader拓展】Illustrative Rendering in Team Fortress 2

    写在前面 早在使用ramp texture控制diffuse光照一文就提到了这篇著名的论文.Valve公司发表的其他成果可见这里.这是Valve在2007年发表的一篇非常具有影响力的文章,我的导师也提 ...

  9. web中间件切换(was切tomcat)

    一.数据源迁移: ①数据源配置在web容器还是在项目本身? 根据开发与生产分离原则选择配置到web容器,以免开发泄露数据库密码. ②数据库密码加密 原先was的数据源直接在console控制,密码是密 ...

  10. Android项目-高考作文项目架构(三)

    上一篇我们讲到了,  Http Json的功能的抽取. 如果我们请求的是一个列表的数据呢? 我们使用那个功能就不是很好. 因为一个列表, 还有很多其他功能(比如每个listView都需要setAdap ...