被 square 公司抛弃的 Otto 框架详解

2,829 阅读8分钟
原文链接: www.jianshu.com

最近讲的都是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用法快速入门

EventBus vs Otto vs Guava--自定义消息总线

EventBus & Otto的使用和比较

浅析Otto框架,并与EventBus对比浅析Otto框架,并与EventBus对比


加油