前言

上一篇文章中讲到了otto的用法,这一篇我们来讲一下otto的源码。可能有人觉得otto过时了,但是通过源码我们学习的是高手设计otto时的设计理念,这种设计理念是不过时的。

otto各个类的作用

首先先来看一下otto的源码的各个类的作用,如下图所示。

如图所示,otto的源码并不多,主要的类的功能如下:

  • Produce、Subscribe:发布者和订阅者注解类。
  • Bus:事件总线类,用来注册和取消注册,维护发布-订阅模型,并处理事件调度分发。
  • HandlerFinder、AnnotatedHandlerFinder:用来查找发布者和订阅者。
  • EventProducer、EventHandler:分别封装发布者和订阅者的数据结构。

otto构造函数

在使用otto时,首先要创建Bus类,Bus类的构造函数如下所示。

public Bus() {this(DEFAULT_IDENTIFIER);
}

View Code

这个DEFAULT_IDENTIFIER是一个字符串”default”,this调用了Bus类的另一个构造函数:

public Bus(String identifier) {this(ThreadEnforcer.MAIN, identifier);}

View Code

ThreadEnforcer.MAIN意味着默认在主线程中调度事件,再往里看this又调用了什么,如下所示。

public Bus(ThreadEnforcer enforcer, String identifier) {this(enforcer, identifier, HandlerFinder.ANNOTATED);
}

View Code

第一个参数我们提到了,就是事件调度的简称,identifier为Bus的名称,默认为”default”。而identifier则是HandlerFinder,用于在register、unregister的时候寻找所有的subscriber和producer。再往里查看this又调用了什么,如下所示。

Bus(ThreadEnforcer enforcer, String identifier, HandlerFinder handlerFinder) {this.enforcer =  enforcer;this.identifier = identifier;this.handlerFinder = handlerFinder;
}

View Code

这个是最终调用的Bus的构造函数,在这里要首先记住handlerFinder 指的就是传进来的HandlerFinder.ANNOTATED,后面在注册时会用到handlerFinder这个属性。

注册

生成bus类后,我们要调用它的register方法来进行注册。register方法如下所示。

public void register(Object object) {if (object == null) {throw new NullPointerException("Object to register must not be null.");}enforcer.enforce(this);Map<Class<?>, EventProducer> foundProducers = handlerFinder.findAllProducers(object);//1
...
}

View Code

注释1出调用了handlerFinder的findAllProducers方法,此前讲到构造函数时,我们知道这个handlerFinder指的是HandlerFinder.ANNOTATED,ANNOTATED的代码如下所示。

HandlerFinder ANNOTATED = new HandlerFinder() {@Overridepublic Map<Class<?>, EventProducer> findAllProducers(Object listener) {return AnnotatedHandlerFinder.findAllProducers(listener);}@Overridepublic Map<Class<?>, Set<EventHandler>> findAllSubscribers(Object listener) {return AnnotatedHandlerFinder.findAllSubscribers(listener);}};

View Code

从上面的代码的findAllProducers方法和findAllSubscribers方法的返回值可以推断出一个注册类只能有一个发布者,却可以有多个订阅者。findAllProducers方法最终调用的是AnnotatedHandlerFinder的findAllProducers方法:

static Map<Class<?>, EventProducer> findAllProducers(Object listener) {final Class<?> listenerClass = listener.getClass();Map<Class<?>, EventProducer> handlersInMethod = new HashMap<Class<?>, EventProducer>();Map<Class<?>, Method> methods = PRODUCERS_CACHE.get(listenerClass);//1if (null == methods) {methods = new HashMap<Class<?>, Method>();loadAnnotatedProducerMethods(listenerClass, methods);//2
  }if (!methods.isEmpty()) {for (Map.Entry<Class<?>, Method> e : methods.entrySet()) {//3EventProducer producer = new EventProducer(listener, e.getValue());handlersInMethod.put(e.getKey(), producer);}}return handlersInMethod;
}

View Code

PRODUCERS_CACHE是一个ConcurrentHashMap,它的key为bus.register()时传入的class,而value是一个map,这个map的key是事件的class,value是生产事件的方法。注释1处首先在PRODUCERS_CACHE根据传入的对象的类型查找是否有缓存的事件方法,如果没有就调用注释2处的代码利用反射去寻找所有使用了@Produce注解的方法,并且将结果缓存到PRODUCERS_CACHE中。接着在注释3处遍历这些事件方法,并为每个事件方法创建了EventProducer类,并将这些EventProducer类作为value存入handlersInMethod并返回。接下来我们返回register方法。如下所示。

public void register(Object object) {if (object == null) {throw new NullPointerException("Object to register must not be null.");}enforcer.enforce(this);Map<Class<?>, EventProducer> foundProducers = handlerFinder.findAllProducers(object);for (Class<?> type : foundProducers.keySet()) {final EventProducer producer = foundProducers.get(type);EventProducer previousProducer = producersByType.putIfAbsent(type, producer);//1if (previousProducer != null) {throw new IllegalArgumentException("Producer method for type " + type+ " found on type " + producer.target.getClass()+ ", but already registered by type " + previousProducer.target.getClass() + ".");}Set<EventHandler> handlers = handlersByType.get(type);if (handlers != null && !handlers.isEmpty()) {for (EventHandler handler : handlers) {dispatchProducerResultToHandler(handler, producer);//2
      }}}...
}

View Code

调用完findAllProducers方法后,会在注释1处检查是否有该类型的发布者已经存在,如果存在则抛出异常,不存在则调用注释2处的dispatchProducerResultToHandler方法来触发和发布者对应的订阅者来处理事件。接下来register方法的后一部分代码就不帖上来了,跟此前的流程大致一样就是调用findAllSubscribers方法来查找所有使用了@Subscribe注解的方法,跟此前不同的是一个注册类可以有多个订阅者,接下来判断是否有该类型的订阅者存在,也就是判断注册类是否已经注册,如果存在则抛出异常,不存在则查找是否有和这些订阅者对应的发布者,如果有的话,就会触发对应的订阅者处理事件。

发送事件

我们会调用Bus的post方法来发送事件,它的代码如下所示。

public void post(Object event) {if (event == null) {throw new NullPointerException("Event to post must not be null.");}enforcer.enforce(this);Set<Class<?>> dispatchTypes = flattenHierarchy(event.getClass());//1boolean dispatched = false;for (Class<?> eventType : dispatchTypes) {Set<EventHandler> wrappers = getHandlersForEventType(eventType);if (wrappers != null && !wrappers.isEmpty()) {dispatched = true;for (EventHandler wrapper : wrappers) {enqueueEvent(event, wrapper);//2
      }}}if (!dispatched && !(event instanceof DeadEvent)) {post(new DeadEvent(this, event));}dispatchQueuedEvents();//3
}

View Code

注释1处的flattenHierarchy方法首先会从缓存中查找传进来的event(消息事件类)的所有父类,如果没有则找到event的所有父类并存储入缓存中。接下来遍历这些父类找到它们的所有订阅者,并在注释2处将这些订阅者压入线程的事件队列中。并在注释3处调用dispatchQueuedEvents方法依次取出事件队列中的订阅者来处理相应event的事件。

取消注册

取消注册时,我们会调用Bus的unregister方法,unregister方法如下所示。

public void unregister(Object object) {if (object == null) {throw new NullPointerException("Object to unregister must not be null.");}enforcer.enforce(this);Map<Class<?>, EventProducer> producersInListener = handlerFinder.findAllProducers(object);//1for (Map.Entry<Class<?>, EventProducer> entry : producersInListener.entrySet()) {final Class<?> key = entry.getKey();EventProducer producer = getProducerForEventType(key);EventProducer value = entry.getValue();if (value == null || !value.equals(producer)) {throw new IllegalArgumentException("Missing event producer for an annotated method. Is " + object.getClass()+ " registered?");}producersByType.remove(key).invalidate();//2
  }
...
}

View Code

取消注册分为两部分,一部分是订阅者取消注册,另一部分是发布者取消注册。这两部分的代码类似,因此,上面的代码只列出了发布者取消注册的代码。在注释1处得到所有使用@Produce注解的方法,并遍历这些方法,调用注释2处的代码从缓存中清除所有和传进来的注册类相关的发布者,来完成发布者的取消注册操作。

转载于:https://www.cnblogs.com/ganchuanpu/p/6058040.html

Android事件总线(四)源码解析otto相关推荐

  1. Spring事件机制Event源码解析(未完待续)

    Spring事件机制Event源码解析(未完待续) 监听器: ApplicationEvent事件 ApplicationListener监听器(观察者) ApplicationEventMultic ...

  2. android handler2--消息队列源码解析

    android handler2–消息队列源码解析 1.Looper 对于Looper主要是prepare()和loop()两个方法. 首先看prepare()方法 public static fin ...

  3. Android Hawk数据库的源码解析,Github开源项目,基于SharedPreferences的的存储框架

    今天看了朋友一个项目用到了Hawk,然后写了这边文章 一.了解一下概念 Android Hawk数据库github开源项目 Hawk是一个非常便捷的数据库.操作数据库只需一行代码,能存任何数据类型. ...

  4. Android手游 “2048” 源码解析

    转载请写明出处:http://blog.csdn.net/big_heart_c 下面所解析的源码是来自极客学院"Android 2048 "中的源码,读者可以从 https:// ...

  5. android网络框架retrofit源码解析二

    注:源码解析文章参考了该博客:http://www.2cto.com/kf/201405/305248.html 前一篇文章讲解了retrofit的annotation,既然定义了,那么就应该有解析的 ...

  6. Android 常用开源框架源码解析 系列 (四)Glide

    一.定义  Glide 一个被google所推荐的图片加载库,作者是bumptech.对Android SDk 最低要求是 API 10  与之功能类似的是Square公司的picasso  二.基本 ...

  7. Android View系列(二):事件分发机制源码解析

    概述 在介绍点击事件规则之前,我们需要知道我们分析的是MotionEvent,即点击事件,所谓的事件分发就是对MotionEvent事件的分发过程,即当一个MotionEvent生成以后,系统需要把这 ...

  8. android 事件分发 代码解析,Android事件分发之源码分析

    原文首发于微信公众号:躬行之,欢迎关注交流! 上篇文章中叙述了 Android 事件分发的大致流程,下面从 Activity.ViewGroup.View 三个方面介绍事件的相关方法,小节如下: Ac ...

  9. 深入解析Android事件分发机制源码(1)

    有关事件分发的文章,网上已经有了太多太多,但是看了很多,大部分都只是讲解了最外层表现给开发者看的结果,并没有深入讲解,为何会得到这个现象.基于透过现象看本质的思想,趁着手头没有太多活,写下这篇博客,一 ...

最新文章

  1. 【BZOJ】2333: [SCOI2011]棘手的操作
  2. mybaits八:select查询返回map集合
  3. Python SSH爆破以及Python3线程池控制线程数
  4. 78、VLAN间路由配置实验之单臂路由
  5. SVN学习总结(1)——SVN简介及入门使用
  6. 【js】获得项目路径
  7. 工作137:map函数
  8. 【转】visual studio 2019 (vs) 显示右侧缩略图_缩略图_滚动条
  9. 如何理解和运用策略模式
  10. access的papersize命令_巡检命令及查看参数
  11. 给Eclipse在线安装WTP
  12. 处于停机等非正常状态_设备非正常停机管理指导办法
  13. 定位相关MATLAB仿真代码与在线MATLAB仿真平台
  14. Spring Bean生命周期总结
  15. WGS-84坐标系转GCJ02坐标系
  16. 【语言环境】win2008R2SP1+WAMP环境部署
  17. 从东京奥运会看js设计模式之发布订阅模式
  18. 【C++】算法STL库
  19. 微信公众号图卡片息发送【模拟发送一个小程序信息】
  20. 微信域名被屏蔽被封了的解决办法 微信网址被屏蔽了红了照样打开

热门文章

  1. python写word表格_使用表格—— 使用Python读写Office文档之三
  2. android 拼接参数,Android 多参数多文件同时上传
  3. java简介 ppt 精,TP1java简介.ppt
  4. js sdk 一键分享 微信_微信朋友圈分享自己拍的视频,一键开启这个设置,自带文字和音乐...
  5. python wordpress xmlrpc 调用_20542通过wordpress_xmlrpc的python包远程操作wordpress 编辑 更新Post...
  6. @value 静态变量_C语言基本知识:变量
  7. 2018北科大计算机学院复试方案,2018年北京交通大学考研复试及录取办法
  8. React Router V6 新特性
  9. Android studio中单项选择,Android studio单选按钮、复选按钮
  10. android 文字垂直居中,前端常见问题——安卓文本无法垂直居中