otto这个开源项目是一个event bus模式的消息框架。用于程序各个模块之间的通信。此消息框架能够使得各个

模块之间降低耦合性。

此项目是支付公司square一个开源项目,项目托管于github

https://github.com/square/otto





基本模型是,Android的组件能够注冊监听,然后发送消息,接收消息。模式就是观察者模式。可是有别于

java实现的观察者模式,otto更具解耦性,通过注解能够现实监听工作。



otto中的总控制中心的类是Bus。复杂事件的注冊分发工作。

首先要把须要把监听事件的组件或者发送事件的组件注冊进去

调用Bus的register方法

public
void
register(Object object)

这种方法的解释是这种。先看下英文

/**
* Registers all handler methods on {@code object} to receive events and producer methods to provide events.
* If any subscribers are registering for types which already have a producer they will be called immediately
* with the result of calling that producer.
* If any producers are registering for types which already have subscribers, each subscriber will be called with
* the value from the result of calling the producer.
*
* @param object object whose handler methods should be registered.
* @throws NullPointerException if the object is null.
*/

參数这个对象的全部的事件处理方法(订阅方法)用于处理接受到的事件,生产者方法用于提供事件。

意思是说。这个注冊者即能够接收事件。也能够生产事件。

接着说了注冊时的一些特殊情况,

(1)假设在注冊的时候,订阅了一个事件类型,而且有产生事件的方法。那么会马上调用产生事件的方法,

把事件分发给这个订阅者方法。

(2)假设在注冊的时候,产生特定事件的方法已经存在了订阅者。会调用事件产生方法,把事件分发给事件

订阅者。

方法的详细实现分析:

public void register(Object object) {
if (object == null) {
throw new NullPointerException("Object to register must not be null.");
}
//检查是否在主线程中进行了注冊的。默认必须在主线程中调用
enforcer.enforce(this); //key是事件的class对象
Map<Class<? >, EventProducer> foundProducers = handlerFinder.findAllProducers(object);

上面的代码用于获取这个被注冊对象的全部生产者方法,能够相应于多个事件的生产者方法

           一个被注冊对象,同一个事件仅仅能注冊一个生产者方法。

 

handlerFinder是HandlerFinder的实例,是Bus的辅助类,用于找到指定被注冊者的全部生产者和订阅者方法。

handlerFinder.findAllProducers(object)返回一个map集合,key是事件的Class的实例,value

是EventProducer是对生产者方法和被注冊者实例的包装。



从这里能够看出,尽管没有继续看这种方法的代码。能够推測被注冊者对于同一个事件仅仅能有一个生产者。

我们继续跟踪HandlerFinder findAllProducers()方法的代码

HandlerFinder仅仅是一个借口,然后在其内部实现了一个内部类

例如以下:

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

会调用到这句代码:

AnnotatedHandlerFinder.findAllProducers(listener)



进去看下代码

static Map<Class<?

>, EventProducer> findAllProducers(Object listener) {
final Class<?> listenerClass = listener.getClass();
Map<Class<?>, EventProducer> handlersInMethod = new HashMap<Class<? >, EventProducer>();
//检查是否不存在此listenerClass的生产者方法。须要找出来放到PRODUCERS_CACHE中
//PRODUCERS_CACHE是一个map,能够是listener的class对象,值是全部的事件生产者方法
if (!PRODUCERS_CACHE.containsKey(listenerClass)) {
loadAnnotatedMethods(listenerClass);
}
//以下的代码就是把全部的生产者方法封装起来。返回给调用者
Map<Class<?>, Method> methods = PRODUCERS_CACHE.get(listenerClass);
if (!methods.isEmpty()) {
for (Map.Entry<Class<?>, Method> e : methods.entrySet()) {
EventProducer producer = new EventProducer(listener, e.getValue());
handlersInMethod.put(e.getKey(), producer);
}
} return handlersInMethod;
}

我们再回到Bus的register方法

//key是事件的class对象
Map<Class<? >, EventProducer> foundProducers = handlerFinder.findAllProducers(object);
for (Class<? > type : foundProducers.keySet()) { //以下几行代码用来检查,事件是否已经注冊过了,一个类一个事件仅仅能注冊一次
final EventProducer producer = foundProducers.get(type);
EventProducer previousProducer = producersByType.putIfAbsent(type, producer);
//checking if the previous producer existed
if (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);
}
}
}

这行代码用于回调

dispatchProducerResultToHandler(handler, producer);

详细实现就是通过反射去调用producer生产出方事件。把事件传递给handler,再通过反射

回调注冊的方法。



继续。。。

//假设有处理此事件的注冊者,回调注冊者
Map<Class<? >, Set<EventHandler>> foundHandlersMap = handlerFinder.findAllSubscribers(object);
for (Class<?> type : foundHandlersMap.keySet()) {
//type变量是事件的class对象
Set<EventHandler> handlers = handlersByType.get(type);
if (handlers == null) {
//concurrent put if absent
Set<EventHandler> handlersCreation = new CopyOnWriteArraySet<EventHandler>();
handlers = handlersByType.putIfAbsent(type, handlersCreation);
if (handlers == null) {
handlers = handlersCreation;
}
}
final Set<EventHandler> foundHandlers = foundHandlersMap.get(type);
handlers.addAll(foundHandlers);
}

regeister这种方法解释完了。



在继续看下post(event)方法

这种方法的作用是分发事件到全部注冊这个事件的方法

有可能这个事件分发失败。会封装一个DeadEvent对象,然后又一次分发。可是这个DeadEvent对象没有被处理。。

public void post(Object event) {
if (event == null) {
throw new NullPointerException("Event to post must not be null.");
}
enforcer.enforce(this);
//返回这个event的全部继承关系链的全部class对象(父类class对象和自己)
Set<Class<? >> dispatchTypes = flattenHierarchy(event.getClass());
//对event家族的全部类的相关注冊方法进行调用
boolean dispatched = false;
for (Class<? > eventType : dispatchTypes) {
//获得和eventType相关的全部注冊方法
Set<EventHandler> wrappers = getHandlersForEventType(eventType);
//把须要回调处理的注冊方法的包装类塞进当前线程处理的队列中去
if (wrappers != null && !wrappers.isEmpty()) {
dispatched = true;
for (EventHandler wrapper : wrappers) {
enqueueEvent(event, wrapper);
}
}
}
//没处理的。再分发一次。可是没有发现再次处理的逻辑
if (!dispatched && !(event instanceof DeadEvent)) {
post(new DeadEvent(this, event));
} dispatchQueuedEvents();
}

接下来就是注销方法unregister(listener)

删除和这个listener对象相关的生产事件的方法和注冊监听的方法

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);
for (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();
}
//返回当前对象的全部注冊的方法。置为无效并删除掉
Map<Class<?>, Set<EventHandler>> handlersInListener = handlerFinder.findAllSubscribers(object);
for (Map.Entry<Class<?>, Set<EventHandler>> entry : handlersInListener.entrySet()) {
//返回相应event的全部注冊方法的包装类
Set<EventHandler> currentHandlers = getHandlersForEventType(entry.getKey());
Collection<EventHandler> eventMethodsInListener = entry.getValue(); if (currentHandlers == null || !currentHandlers.containsAll(eventMethodsInListener)) {
throw new IllegalArgumentException(
"Missing event handler for an annotated method. Is " + object.getClass()
+ " registered? ");
} for (EventHandler handler : currentHandlers) {
if (eventMethodsInListener.contains(handler)) {
handler.invalidate();
}
}
currentHandlers.removeAll(eventMethodsInListener);
}
}

最后总结一下,

(1)同一个事件仅仅能有一个生产此事件的方法,假设存在多个会报非检查异常,产生此类事件的仅仅同意有一个来源或者说仅仅同意

有一个活者的来源。

(2)同一个事件能够有多个注冊监听同一个事件的方法,仅仅要存在就会分发给他们。

(3)依照官方的demo尽量仅仅有一个Bus实例。一是降低内存消耗,也利于分发工作,假设不同的Bus,那么就无法把分发给其它Bus的注冊监听的方法了。

这个EventBus消息框架比較适合推送的处理中心对消息的分发工作,能够解耦的方式,分发给程序的各个模块。



square公司还有非常多比較好的开源项目。

网络库okhttp初支持http外,还支持spdy,github地址https://github.com/square/okhttp

处理图片的库picasso(毕加索) ,github地址 https://github.com/square/picasso

一个日历控件库,包括Android版和iOS版, github地址 https://github.com/square/android-times-square (Android版)。

其他square公司的开源项目:https://github.com/square

otto源代码分析的更多相关文章

  1. android-plugmgr源代码分析

    android-plugmgr是一个Android插件加载框架,它最大的特点就是对插件不需要进行任何约束.关于这个类库的介绍见作者博客,市面上也有一些插件加载框架,但是感觉没有这个好.在这篇文章中,我 ...

  2. Twitter Storm源代码分析之ZooKeeper中的目录结构

    徐明明博客:Twitter Storm源代码分析之ZooKeeper中的目录结构 我们知道Twitter Storm的所有的状态信息都是保存在Zookeeper里面,nimbus通过在zookeepe ...

  3. 转:SDL2源代码分析

    1:初始化(SDL_Init()) SDL简介 有关SDL的简介在<最简单的视音频播放示例7:SDL2播放RGB/YUV>以及<最简单的视音频播放示例9:SDL2播放PCM>中 ...

  4. 转:RTMPDump源代码分析

    0: 主要函数调用分析 rtmpdump 是一个用来处理 RTMP 流媒体的开源工具包,支持 rtmp://, rtmpt://, rtmpe://, rtmpte://, and rtmps://. ...

  5. 转:ffdshow 源代码分析

    ffdshow神奇的功能:视频播放时显示运动矢量和QP FFDShow可以称得上是全能的解码.编码器.最初FFDShow只是mpeg视频解码器,不过现在他能做到的远不止于此.它能够解码的视频格式已经远 ...

  6. UiAutomator源代码分析之UiAutomatorBridge框架

    上一篇文章<UIAutomator源代码分析之启动和执行>我们描写叙述了uitautomator从命令行执行到载入測试用例执行測试的整个流程.过程中我们也描写叙述了UiAutomatorB ...

  7. MyBatis架构设计及源代码分析系列(一):MyBatis架构

    如果不太熟悉MyBatis使用的请先参见MyBatis官方文档,这对理解其架构设计和源码分析有很大好处. 一.概述 MyBatis并不是一个完整的ORM框架,其官方首页是这么介绍自己 The MyBa ...

  8. hostapd源代码分析(三):管理帧的收发和处理

    hostapd源代码分析(三):管理帧的收发和处理 原文链接:http://blog.csdn.net/qq_21949217/article/details/46004379 这篇文章我来讲解一下h ...

  9. hostapd源代码分析(二):hostapd的工作机制

    [转]hostapd源代码分析(二):hostapd的工作机制 原文链接:http://blog.csdn.net/qq_21949217/article/details/46004433 在我的上一 ...

随机推荐

  1. POJ 1185 炮兵阵地 (状压dp)(棋盘dp)

    这题和poj 3254很像,但是更复杂了一些 都属于棋盘里放东西,然后又各种各样的限制,然后求方案或者最大值 (1)上一道题距离要大于1,这道题是大于2.所以判断的时候变成 !(x & (x ...

  2. 关于Vue实例的生命周期(2)

     关于Vue实例的生命周期(2) 创建(create)->挂载(mount)->更新(update)->销毁(destory) 钩子函数触发事件 beforeCreate 在实例初始 ...

  3. 手写一个节点大小平衡树(SBT)模板,留着用

    看了一下午,感觉有了些了解.应该没有错,有错希望斧正,感谢 #include<stdio.h> #include<string.h> struct s { int key,le ...

  4. Activity嵌套多个Fragment实现横竖屏切换

    一.上图 二.需求 最近项目遇到个横竖屏切换的问题.较为复杂.在此记之. 1.Activity中竖屏嵌套3个Fragment,本文简称竖屏FP1,FP2,FP3. 2.当中竖屏FP1与FP2能够切换为 ...

  5. OSGI项目中获取文件路径

    假设想依据给定的文件名创建一个File实例,你可能会这么写: File file = new File(当前类.class.getResource("config").toURI( ...

  6. hdu 5335 Walk Out 搜索+贪心

    Walk Out Time Limit: 2000/1000 MS (Java/Others)    Memory Limit: 65536/65536 K (Java/Others) Total S ...

  7. jqGrid添加删除功能(不和数据库交互)

    jqGrid添加删除功能(不和数据库交互) 一.背景需求 项目中需要在前端页面动态的添加行,删除行,上下移动行等,同时还不和数据库交互.一直在用jqGrid展示表格的我们,从没有深入的研究过它,当然看 ...

  8. Activiti 23张表及7大服务详解

    7大服务介绍 服务名称 描述 RepositoryService Activiti 中每一个不同版本的业务流程的定义都需要使用一些定义文件,部署文件和支持数据 ( 例如 BPMN2.0 XML 文件, ...

  9. Javascript平稳退化、渐进增强

    平稳退化 : javascript平稳退化就是如果一个浏览器完全不支持js或者禁用js的时候,它的基本功能不会受到任何影响.比方说一个网站使用了大量javascript来优化页面,我们现在把浏览器的j ...

  10. SpringBoot学习笔记(4)----SpringBoot中freemarker、thymeleaf的使用

    1. freemarker引擎的使用 如果你使用的是idea或者eclipse中安装了sts插件,那么在新建项目时就可以直接指定试图模板 如图: 勾选freeMarker,此时springboot项目 ...