[Android组件解读] 被square公司抛弃的Otto框架详解

最近讲的都是square出品的组件,今天还是继续讲解Square旗下的Otto框架

引用otto官网说的

An enhanced Guava-based event bus with emphasis on Android support.

Otto is an event bus designed to 
decouple different parts of your application 
while still allowing them to communicate efficiently.

Forked from Guava, 
Otto adds unique functionality to an already refined
event bus as well as specializing it to the Android platform.

Otto是基于Google的Guava(没听说过??)的Android事件总线,如果在Android项目中想进行组件的通信可以使用这个库,可以降低程序之间的耦合性。

科普Guava:

Guava是Google基于Java1.6的类库集合扩展项目。
提供了collections,caching,eventbus高质量的API来使你的Java代码更加优美

Guava的EventBus首先实现了一个基于发布订阅的消息类库,默认以注解来查找订阅者

Guava相关资料:

Guava学习笔记:Google Guava 类库简介

Google Guava官方教程(中文版)

源码包的简单说明: 
  com.google.common.annotations:普通注解类型。 
  com.google.common.base:基本工具类库和接口。 
  com.google.common.cache:缓存工具包,非常简单易用且功能强大的JVM内缓存。 
  com.google.common.collect:带泛型的集合接口扩展和实现,以及工具类,这里你会发现很多好玩的集合。 
  com.google.common.eventbus:发布订阅风格的事件总线。 
  com.google.common.hash: 哈希工具包。 
  com.google.common.io:I/O工具包。 
  com.google.common.math:原始算术类型和超大数的运算工具包。 
  com.google.common.net:网络工具包。 
  com.google.common.primitives:八种原始类型和无符号类型的静态工具包。 
  com.google.common.reflect:反射工具包。 
  com.google.common.util.concurrent:多线程工具包。

Otto已经快要被Square废弃了,引用下官网上的原话

Deprecated!

This project is deprecated in favor of RxJava and RxAndroid. 
These projects permit the same event-driven programming model as Otto, 
but they’re more capable and offer better control of threading.

If you’re looking for guidance on migrating from Otto to Rx, 
this post is a good start.

Square推荐使用RxJava实现RxBus方式来代替Otto。

因为原来的项目是用的是otto,至少目前otto没有被开发者抛弃。

本文还是介绍下Otto的使用以及原理。

Otto使用姿势

  1. 首先引入AAR包
// Otto
compile 'com.squareup:otto:1.3.8'
  1. 构造Bus单例
public class BusProvider {

    private static final Bus bus = new Bus();

    public static Bus getBus(){
        return bus;
    }
}
    ``` 

3.在代码中实现(只是举个例子,Otto可以不在同一个类中进行通信)

    public class MainActivity extends AppCompatActivity {
    
        @Override
        protected void onCreate(Bundle savedInstanceState) {
            super.onCreate(savedInstanceState);
            setContentView(R.layout.activity_main);
    
            Button button = (Button) this.findViewById(R.id.post_event);
            button.setOnClickListener(new View.OnClickListener() {
                @Override
                public void onClick(View v) {
                       // 发送事件
                    BusEvent busEvent = new BusEvent("TODO");
                    BusProvider.getBus().post(busEvent);
                }
            });
        }
    
        @Override
        protected void onResume() {
            super.onResume();
    
            // 注册事件
            BusProvider.getBus().register(this);
        }
    
        @Override
        protected void onPause() {
            super.onPause();
        }
    
        @Override
        protected void onStop() {
            super.onStop();
    
            // 注销事件
            BusProvider.getBus().unregister(this);
        }
        
        
        /**
         * 接收事件
         * @param event
         */
        @Subscribe
        public void getEventBus(BusEvent event) {
            String key = event.getEventKey();
            Toast.makeText(MainActivity.this,"key = " + key,Toast.LENGTH_LONG).show();
        }
    }

上述看到了其实用法很简单,就是注册,注销,发送事件,接收事件。

> 这里需要提到一点,发送跟接受可以不再同一个类中,但是接受的地方一定要注册时间和注销事件。

#### 重要知识点
定义Subcribe的方法需要记住
    
    参数类型的数量只允许为1个,则抛出异常
    如果第一个参数的类型为接口,则抛出异常
    方法的公开类型必须为public,private则抛出异常
    
定义Produce的方法需要记住
    
    Produce的参数如果不等于0,则抛出异常
    Produce的返回值类型如果为void,否则抛出异常
    Produce的返回值类型如果为接口类型,否则抛出异常
    Produce的方法的公开类型必须为public,private则抛出异常
    
##### post时候,会根据传入的参数类型去查找跟他一致的参数类型的@Subcribe方法

> 其实Otto使用的时候条条框框真的很多


`抛出一个问题?Otto是通过什么方法来进行线程间通信的?`

带着问题,我们看下代码

### 代码讲解
老规矩,还是从代码架构来看。这应该是最近看的最少的类了。

    .
    ├── AnnotatedHandlerFinder.java --用来查找存在@Subcribe和@Produce的类的方法
    ├── Bus.java   --对外提供者,提供register,unregister,post等方法
    ├── DeadEvent.java  --发送这个Event则不会进行Post
    ├── EventHandler.java  --@Subcribe的方法和类的实体对象
    ├── EventProducer.java --@Produce的方法和类的实体对象
    ├── HandlerFinder.java --查找存在@ Subcribe和@Produce的类的方法的接口
    ├── Produce.java   --Produce注解定义
    ├── Subscribe.java --Subscribe注解定义
    └── ThreadEnforcer.java --线程强迫,默认提供MAIN线程,不是主线程,就抛出异常
    
#### Bus的构造器
开始要构造Bus的对象
    
    Bus bus = new Bus();
    
这个其实走的全是默认值,具体默认值有哪些,看下面代码
    
    public Bus() {
        this(DEFAULT_IDENTIFIER);
    }

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

    public Bus(ThreadEnforcer enforcer) {
        this(enforcer, DEFAULT_IDENTIFIER);
    }

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

    /**
     * BUS构造器
     * @param enforcer    线程判断,默认MAIN,不是主线程则抛出异常
     * @param identifier  BUS定义
     * @param handlerFinder 查找 @Subcribe和 @Produce的类的方法的实现类
     */
    Bus(ThreadEnforcer enforcer, String identifier, HandlerFinder handlerFinder) {

        // 默认ThreadEnforcer.MAIN,只允许在主线程
        this.enforcer = enforcer;

        // 默认定义"default"
        this.identifier = identifier;

        // 提供查找 @Subcribe和 @Produce的类的方法. 实现下面方法
        //  Map<Class<?>, EventProducer> findAllProducers(Object listener);
        // Map<Class<?>, Set<EventHandler>> findAllSubscribers(Object listener);
        this.handlerFinder = handlerFinder;

    }

### BUS的Register方法

    Bus bus = new Bus();
    bus.register(this)
    
从代码中看出有什么玄机?

    /**
     * BUS的注解方法
     * @param object
     */
    public void register(Object object) {
        if (object == null) {
            throw new NullPointerException("Object to register must not be null.");
        }
        // 强迫判断,提供类两个对象,ANY和MAIN,
        // MAIN表示当前只允许在主线程
        // ANY表示任何线程都是支持
        enforcer.enforce(this);

        // 获取当前Object对象中@Produce的方法
        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) {
                    // 将@Produce生成的扔给@Subcribe进行运行
                    dispatchProducerResultToHandler(handler, producer);
                }
            }
        }

        // 获取当前Object对象中的@Subcribe的方法
        Map<Class<?>, Set<EventHandler>> foundHandlersMap = handlerFinder.findAllSubscribers(object);
        for (Class<?> type : foundHandlersMap.keySet()) {
            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);
            if (!handlers.addAll(foundHandlers)) {
                throw new IllegalArgumentException("Object already registered.");
            }
        }

        for (Map.Entry<Class<?>, Set<EventHandler>> entry : foundHandlersMap.entrySet()) {
            Class<?> type = entry.getKey();
            EventProducer producer = producersByType.get(type);
            if (producer != null && producer.isValid()) {
                Set<EventHandler> foundHandlers = entry.getValue();
                for (EventHandler foundHandler : foundHandlers) {
                    if (!producer.isValid()) {
                        break;
                    }
                    if (foundHandler.isValid()) {

                        // 将@Produce生成的扔给@Subcribe进行运行
                        dispatchProducerResultToHandler(foundHandler, producer);
                    }
                }
            }
        }
    }

以下介绍如何将类中定义的`@Subcribe`和`@Produce`方法找到

    private static void loadAnnotatedMethods(Class<?> listenerClass,
                                             Map<Class<?>, Method> producerMethods, Map<Class<?>, Set<Method>> subscriberMethods) {
        // 获取当前类中的方法
        for (Method method : listenerClass.getDeclaredMethods()) {
            // The compiler sometimes creates synthetic bridge methods as part of the
            // type erasure process. As of JDK8 these methods now include the same
            // annotations as the original declarations. They should be ignored for
            // subscribe/produce.
            // 是否存在桥接方法
            if (method.isBridge()) {
                continue;
            }
            // 查找方法是否以@Subscribe注解
            if (method.isAnnotationPresent(Subscribe.class)) {
                Class<?>[] parameterTypes = method.getParameterTypes();
                // 参数类型的数量只允许为1个,则抛出异常
                if (parameterTypes.length != 1) {
                    throw new IllegalArgumentException("Method " + method + " has @Subscribe annotation but requires "
                            + parameterTypes.length + " arguments.  Methods must require a single argument.");
                }

                Class<?> eventType = parameterTypes[0];
                // 如果第一个参数的类型为接口,则抛出异常
                if (eventType.isInterface()) {
                    throw new IllegalArgumentException("Method " + method + " has @Subscribe annotation on " + eventType
                            + " which is an interface.  Subscription must be on a concrete class type.");
                }

                // 方法的公开类型必须为public,private则抛出异常
                if ((method.getModifiers() & Modifier.PUBLIC) == 0) {
                    throw new IllegalArgumentException("Method " + method + " has @Subscribe annotation on " + eventType
                            + " but is not 'public'.");
                }

                Set<Method> methods = subscriberMethods.get(eventType);
                if (methods == null) {
                    methods = new HashSet<Method>();
                    subscriberMethods.put(eventType, methods);
                }
                methods.add(method);
            } else if (method.isAnnotationPresent(Produce.class)) {
                Class<?>[] parameterTypes = method.getParameterTypes();
                // Produce的参数如果不等于0,则抛出异常
                if (parameterTypes.length != 0) {
                    throw new IllegalArgumentException("Method " + method + "has @Produce annotation but requires "
                            + parameterTypes.length + " arguments.  Methods must require zero arguments.");
                }
                // Produce的返回值类型如果为Void,否则抛出异常
                if (method.getReturnType() == Void.class) {
                    throw new IllegalArgumentException("Method " + method
                            + " has a return type of void.  Must declare a non-void type.");
                }

                Class<?> eventType = method.getReturnType();
                // Return的类型为接口的话,否者抛出异常
                if (eventType.isInterface()) {
                    throw new IllegalArgumentException("Method " + method + " has @Produce annotation on " + eventType
                            + " which is an interface.  Producers must return a concrete class type.");
                }

                // Produce的返回值类型如果为void,否则抛出异常
                if (eventType.equals(Void.TYPE)) {
                    throw new IllegalArgumentException("Method " + method + " has @Produce annotation but has no return type.");
                }

                // 方法的公开类型必须为public,private则抛出异常
                if ((method.getModifiers() & Modifier.PUBLIC) == 0) {
                    throw new IllegalArgumentException("Method " + method + " has @Produce annotation on " + eventType
                            + " but is not 'public'.");
                }

                if (producerMethods.containsKey(eventType)) {
                    throw new IllegalArgumentException("Producer for type " + eventType + " has already been registered.");
                }
                producerMethods.put(eventType, method);
            }
        }

        PRODUCERS_CACHE.put(listenerClass, producerMethods);
        SUBSCRIBERS_CACHE.put(listenerClass, subscriberMethods);
    }
    
`register就是将各个注册的@Subcribe和@Produce的方法放到缓存里`

### Bus的post方法

就是根据post(Object),根据Object对应的Class去找注册进来的@Subcribe中的方法的参数类型。将一致的方法列表弄成队列,然后依次执行 

    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());

        boolean dispatched = false;
        for (Class<?> eventType : dispatchTypes) {
            Set<EventHandler> wrappers = getHandlersForEventType(eventType);

            if (wrappers != null && !wrappers.isEmpty()) {
                dispatched = true;
                for (EventHandler wrapper : wrappers) {
                    // 将Post的参数对应的Subcribe方法中对应的参数的方法放到队列中
                    enqueueEvent(event, wrapper);
                }
            }
        }

        if (!dispatched && !(event instanceof DeadEvent)) {
            post(new DeadEvent(this, event));
        }
        // 将上面的队列中保存的方法 依次发送事件
        dispatchQueuedEvents();
    }

>执行方法(就是通过反射实现的,线程间通信)
>
>重点就是这句 method.invoke(target, event);

      public void handleEvent(Object event) throws InvocationTargetException {
    if (!valid) {
      throw new IllegalStateException(toString() + " has been invalidated and can no longer handle events.");
    }
    try {
      method.invoke(target, event);
    } catch (IllegalAccessException e) {
      throw new AssertionError(e);
    } catch (InvocationTargetException e) {
      if (e.getCause() instanceof Error) {
        throw (Error) e.getCause();
      }
      throw e;
    }
  }
  

### Bus的unregister方法

unregister(object)就是从缓存中删除这个object对应的@Subcribe和@Produce的方法
    
    
    public void unregister(Object object) {
        if (object == null) {
            throw new NullPointerException("Object to unregister must not be null.");
        }
        enforcer.enforce(this);

        // 清空该Object中的@Produce方法
        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()) {
            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();
                }
            }
            // 清空该Object中的@Subcribe方法
            currentHandlers.removeAll(eventMethodsInListener);
        }
    }

### 相关资料
[Android 事件总线OTTO用法快速入门](http://blog.csdn.net/zhangweiwtmdbf/article/details/49096615)

[EventBus vs Otto vs Guava--自定义消息总线](http://www.cnblogs.com/avenwu/archive/2015/02/25/4299258.html)

[EventBus & Otto的使用和比较](http://www.jianshu.com/p/cb39a0018db1)

[浅析Otto框架,并与EventBus对比浅析Otto框架,并与EventBus对比](http://mp.weixin.qq.com/s?__biz=MjM5NDkxMTgyNw==&mid=2653057414&idx=1&sn=0fd708aad27ec6184a69d1078964a9e3#wechat_redirect&utm_source=tuicool&utm_medium=referral)


![加油](http://upload-images.jianshu.io/upload_images/1863168-4eed6f8cbdf2d0a0.jpg?imageMogr2/auto-orient/strip%7CimageView2/2/w/1240)
最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 194,761评论 5 460
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 81,953评论 2 371
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 141,998评论 0 320
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 52,248评论 1 263
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 61,130评论 4 356
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 46,145评论 1 272
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 36,550评论 3 381
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 35,236评论 0 253
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 39,510评论 1 291
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 34,601评论 2 310
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 36,376评论 1 326
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 32,247评论 3 313
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 37,613评论 3 299
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 28,911评论 0 17
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 30,191评论 1 250
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 41,532评论 2 342
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 40,739评论 2 335

推荐阅读更多精彩内容