zoukankan      html  css  js  c++  java
  • otto源代码分析

    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




  • 相关阅读:
    PAT 甲级 1113 Integer Set Partition
    简单 dp
    #Leetcode# 707. Design Linked List
    #Leetcode# 817. Linked List Components
    #Leetcode# 268. Missing Number
    #Leetcode# 328. Odd Even Linked List
    #Leetcode# 445. Add Two Numbers II
    jzoj 1252. 天平
    2019.01.26【NOIP提高组】模拟 B 组 总结
    【NOIP2011DAY2】计算系数
  • 原文地址:https://www.cnblogs.com/cxchanpin/p/7050568.html
Copyright © 2011-2022 走看看