基于观察者模式-----otto源码分析

一、otto简介

otto是支付公司square一个专注于android的开源项目,该项目是一个event bus模式的消息框架,是一个基于Guava的增强型事件总线。旨在将应用程序的不同部分分离,同时仍然允许它们进行高效通信。也就是用于程序各个模块之间的通信。

项目链接:https://github.com/square/otto/tree/master/otto/src/main/java/com/squareup/otto

二、otto与观察者模式

在这之前,我们需要先了解一下什么是event bus?

我们在写程序的时候,通常需要让应用程序的各个组件之间进行消息传递(通信),各个组件之间不得不发生联系。但是当项目变得越来越大越来越复杂的时候,使用各种方法进行模块间通信、模块与后台线程进行通信时,往往代码量很大,而且他们的通信也导致了各个模块之间是高度耦合的。

EventBus可以代替Android传统的Intent,Handler,Broadcast或接口函数,在Fragment,Activity,Service线程之间传递数据,执行方法。https://baike.baidu.com/item/EventBus/20461274

也就是说,EventBus能够让各组件间的通信变得更简单,能有效的分离事件发送方和接收方,降低组件之间的耦合度,能避免复杂和容易出错的依赖性以及生命周期问题,同时也使代码变得更简洁,提高可读性,性能也更好。

otto是基于Guava的增强型事件总线(event bus)。通过发布和订阅事件,实际是注解和反射技术而实现的观察者模式。

观察者模式是一个使用率非常高的模式,它最常用在GUI系统、订阅–发布系统。举个例子:

我们平时都会有自己感兴趣的东西,假设我们对某些电视剧很感兴趣,我们就可以向视频软件订阅这些电视剧的开播消息,当有这些电视剧快要开播了,视频软件就马上将这些消息通知到我们。在这个例子中,“我们”是观察者,“开播消息”是被观察者。被观察者一旦发生变动,就主动通知观察者。

在这个例子中,我们可以采用一些手段来管理这些开播消息,以减少组件之间的耦合。这就是观察者模式。观察者模式可以将被观察者和观察者解耦,使得它们之间的依赖性更小,甚至做到毫无依赖。比如说,观察者模式可以通过一些抽象类或者接口,来减少实体类之间的联系,以此做到解耦合。

所以otto利用观察者模式有效的减少了各组件之间的耦合度。

三、otto源码解析

otto中一共有九个.java文件:

(1)AnnotatedHandlerFinder 注解解析器:

这个类用来负责通过反射获取 通过 @Produce 注释的方法和通过 @Subscribe 注释的方法。

(2)Bus 总线核心类

(3) DeadEvent 没有接收者的事件:

包裹一个已经提交的但是由于没有订阅者而不能发出的事件。

(4)EventHandler 事件订阅者

(5)EventProducer 事件生产者

(6)HandlerFinder 获取接收者生产者:

本身是一个接口,用于发现生产者和订阅者。

(7)Produce 生产者的注解:

标记一个方法作为实例生产者,被AnnotatedHandlerFinder和Bus类使用

(8)Subscribe 订阅者注:

将方法标记作为一个事件处理方法,同Produce一样也是在AnnotatedHandlerFinder和Bus类中使用。

(9)ThreadEnforcer对线程进行校验:

本身是一个接口,其内部有一个接口方法。otto确保收到的回调总是在自己想要的线程上,默认一个实例的所有交互都是局限于主线程上的。

要分析源代码,我们从这个库是怎么使用的来入手。我们在使用的过程中,最常用也是直接使用的其实是Bus这个类

Bus 总线核心类

Bus.java将事件调度给侦听器,并为侦听器提供注册自己的方法。总线允许组件之间的发布 - 订阅式通信,而不需要组件彼此明确地注册(因此彼此了解)。 它专门用于使用显式注册或侦听器替换传统的Android进程内事件分发。 它不是通用发布 - 订阅系统,也不是用于进程间通信。

(1)首先利用Bus()构造函数在整个app中创建一个单例,这样不但节省资源,还能保证消息正常到达。Bus的构造函数如下:

 1 public class Bus {
 2   public static final String DEFAULT_IDENTIFIER = "default";
 3 
 4   /** All registered event handlers, indexed by event type. */
 5   private final ConcurrentMap<Class<?>, Set<EventHandler>> handlersByType =
 6           new ConcurrentHashMap<Class<?>, Set<EventHandler>>();
 7 
 8   /** All registered event producers, index by event type. */
 9   private final ConcurrentMap<Class<?>, EventProducer> producersByType =
10           new ConcurrentHashMap<Class<?>, EventProducer>();
11 
12   /** Identifier used to differentiate the event bus instance. */
13   private final String identifier;
14 
15   /** Thread enforcer for register, unregister, and posting events. */
16   private final ThreadEnforcer enforcer;
17 
18   /** Used to find handler methods in register and unregister. */
19   private final HandlerFinder handlerFinder;
20 
21   /** Queues of events for the current thread to dispatch. */
22   private final ThreadLocal<ConcurrentLinkedQueue<EventWithHandler>> eventsToDispatch =
23       new ThreadLocal<ConcurrentLinkedQueue<EventWithHandler>>() {
24         @Override protected ConcurrentLinkedQueue<EventWithHandler> initialValue() {
25           return new ConcurrentLinkedQueue<EventWithHandler>();
26         }
27       };
28 
29   /** True if the current thread is currently dispatching an event. */
30   private final ThreadLocal<Boolean> isDispatching = new ThreadLocal<Boolean>() {
31     @Override protected Boolean initialValue() {
32       return false;
33     }
34   };
35 
36   /** Creates a new Bus named "default" that enforces actions on the main thread. */
37   public Bus() {
38     this(DEFAULT_IDENTIFIER);
39   }
40 
41   /**
42    * Creates a new Bus with the given {@code identifier} that enforces actions on the main thread.
43    *
44    * @param identifier a brief name for this bus, for debugging purposes.  Should be a valid Java identifier.
45    */
46   public Bus(String identifier) {
47     this(ThreadEnforcer.MAIN, identifier);
48   }
49 
50   /**
51    * Creates a new Bus named "default" with the given {@code enforcer} for actions.
52    *
53    * @param enforcer Thread enforcer for register, unregister, and post actions.
54    */
55   public Bus(ThreadEnforcer enforcer) {
56     this(enforcer, DEFAULT_IDENTIFIER);
57   }
58 
59   /**
60    * Creates a new Bus with the given {@code enforcer} for actions and the given {@code identifier}.
61    *
62    * @param enforcer Thread enforcer for register, unregister, and post actions.
63    * @param identifier A brief name for this bus, for debugging purposes.  Should be a valid Java identifier.
64    */
65   public Bus(ThreadEnforcer enforcer, String identifier) {
66     this(enforcer, identifier, HandlerFinder.ANNOTATED);
67   }
68 
69   /**
70    * Test constructor which allows replacing the default {@code HandlerFinder}.
71    *
72    * @param enforcer Thread enforcer for register, unregister, and post actions.
73    * @param identifier A brief name for this bus, for debugging purposes.  Should be a valid Java identifier.
74    * @param handlerFinder Used to discover event handlers and producers when registering/unregistering an object.
75    */
76   Bus(ThreadEnforcer enforcer, String identifier, HandlerFinder handlerFinder) {
77     this.enforcer =  enforcer;
78     this.identifier = identifier;
79     this.handlerFinder = handlerFinder;
80   }
81 
82   @Override public String toString() {
83     return "[Bus \"" + identifier + "\"]";
84   }
Bus的构造函数

在构造方法中,有两个参数:

其中,ThreadEnforcer -- 这个是确保bus运行在指定的线程

HandlerFinder -- 这个是用来提前事件生产者和操作者的注册和取消注册的

(2)构造之后,需要把监听事件的组件或者发送事件的组件注册进去==>调用Bus的register方法。在类创建好之后,或者需要重新注册的时候注册,一般在Activity的onCreate()或者onPause()方法中。

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

需要注册所有事件处理方法方法以接收事件,以及生产者方法以提供事件。
    *如果任何订阅者正在注册已经拥有生产者的方法,则会立即调用该生产者。
    *如果正在注册的生产者已经拥有订阅者,则将调用事件产生方法,把事件分发给事件订阅者。

从上面的代码可以看出:首先获取所有的@Producer注释的方法,然后获取所有的@Subscribe注释的函数,最后将两者关联起来,这样,在调用post()方法时,就能实现数据传递的效果了。

在注册对象之后,会解析出对象对应的类的生产方法和订阅方法,订阅者解析的结果保存在handlersByType,生产者解析的结果保存在producersByType里对象注册首先进行了非空校验,然后是线程的校验。
    resister中使用put方法,enforcer.enforce(this)这句是用来确保运行的线程的。foundProducers是用来发现所有的生产者。foundHandlersMap这个用来获取所有的订阅者。

 (3)定义订阅方法

这个方法自己实现,看具体功能。

(4)发送消息

发送消息需要调用Bus的post()方法。post()分发事件到所有注册这个事件的方法中去。如果事件分发失败,会封装一个DeadEvent对象,然后重新分发,但是这个DeadEvent对象没有被处理。

 1 public void post(Object event) {
2     if (event == null) {
3       throw new NullPointerException("Event to post must not be null.");
4     }
5     enforcer.enforce(this);
6
7     Set<Class<?>> dispatchTypes = flattenHierarchy(event.getClass());
8
9     boolean dispatched = false;
10     for (Class<?> eventType : dispatchTypes) {
11       Set<EventHandler> wrappers = getHandlersForEventType(eventType);
12
13       if (wrappers != null && !wrappers.isEmpty()) {
14         dispatched = true;
15         for (EventHandler wrapper : wrappers) {
16           enqueueEvent(event, wrapper);
17         }
18       }
19     }
20
21     if (!dispatched && !(event instanceof DeadEvent)) {
22       post(new DeadEvent(this, event));
23     }
24
25     dispatchQueuedEvents();
26   }

其中,Set<Class<?>> dispatchTypes = flattenHierarchy(event.getClass())返回这个event的所有继承关系链的所有class对象(父类class对象和自己); Set<EventHandler> wrappers = getHandlersForEventType(eventType)这句是用来获得和eventType相关的注册方法。

总的来说:当调用  public void post(Object event)这个方法之后,首先进行线程校验,然后解析出对应的订阅者,如果有订阅者,将event放入队列中, 如果没有,就作为一个DeadEvent。

Bus在分发消息之后循环从消息队列中取值,这跟android的handler消息机制很像,不过bus中的循环在消息取完之后就结束了。消息队列使用ThreadLocal保证了队列的独立性。同时多个线程会创建多个循环,提高了效率。发送的消息很快就就可以分发。

(5)解绑(注销)

解绑也就是调用注销方法unregister(listener),删除和这个listener对象相关的生产事件的方法和注册监听的方法。

代码如下:

复制代码
 1 public void unregister(Object object) {
 2     if (object == null) {
 3       throw new NullPointerException("Object to unregister must not be null.");
 4     }
 5     enforcer.enforce(this);
 6 
 7     Map<Class<?>, EventProducer> producersInListener = handlerFinder.findAllProducers(object);
 8     for (Map.Entry<Class<?>, EventProducer> entry : producersInListener.entrySet()) {
 9       final Class<?> key = entry.getKey();
10       EventProducer producer = getProducerForEventType(key);
11       EventProducer value = entry.getValue();
12 
13       if (value == null || !value.equals(producer)) {
14         throw new IllegalArgumentException(
15             "Missing event producer for an annotated method. Is " + object.getClass()
16                 + " registered?");
17       }
18       producersByType.remove(key).invalidate();
19     }
20 
21     Map<Class<?>, Set<EventHandler>> handlersInListener = handlerFinder.findAllSubscribers(object);
22     for (Map.Entry<Class<?>, Set<EventHandler>> entry : handlersInListener.entrySet()) {
23       Set<EventHandler> currentHandlers = getHandlersForEventType(entry.getKey());
24       Collection<EventHandler> eventMethodsInListener = entry.getValue();
25 
26       if (currentHandlers == null || !currentHandlers.containsAll(eventMethodsInListener)) {
27         throw new IllegalArgumentException(
28             "Missing event handler for an annotated method. Is " + object.getClass()
29                 + " registered?");
30       }
31 
32       for (EventHandler handler : currentHandlers) {
33         if (eventMethodsInListener.contains(handler)) {
34           handler.invalidate();
35         }
36       }
37       currentHandlers.removeAll(eventMethodsInListener);
38     }
39   }

跟register类似,首先是对象非空校验,然后是线程校验,然后解绑,注册跟解绑一定要成对,没有注册不可以解绑,解绑之后不可以直接再次解绑。

四、otto使用观察者模式的优缺点

otto多数实现依赖注解反射。android事件总线处理还有个很好的开源框架是EventBus,EventBus稍微重量级一些,复杂一些,对应的功能更多,对线程控制更加灵活。

可以看到观察者模式有以下优点:

(1)观察者模式解除了主题和具体观察者的耦合,让耦合的双方都依赖于抽象,而不是依赖具体。从而使得各自的变化都不会影响另一边的变化,这样就可以很好的应对业务变化。

(2)增强了系统的灵活性和可扩展性。

缺点:

在应用观察者模式时需要考虑一下开发效率和运行效率的问题,程序中包括一个被观察者和多个观察者,开发调试等内容会比较复杂,而且在Java中消息的通知默认是顺序执行的,一个观察者卡顿,会影响整体的执行效率,在这种情况下一般考虑采用异步的方式。

参考博客:https://blog.csdn.net/robertcpp/article/details/51628749?utm_source=blogxgwz11

 
 
 

猜你喜欢

转载自www.cnblogs.com/wanlwen/p/9833345.html