学习EventBus之订阅

相信做Android开发的很少有没听说过EventBus的,如果真没听过那只能说明你Out了。EventBus是针对Android平台优化了的发布/订阅事件总线,它的目的是简化Android组件以及线程间的通信,从而使代码更加简洁。

官方给的流程图

《学习EventBus之订阅》

简单使用

  • 定义事件类型
    public class MessageEvent{}
  • 订阅者订阅事件
    eventBus.register(this)
  • 发送事件
    eventBus.post(messageEvent)

通过以上三步,就完成一次完整的EventBus使用流程。

其实类似这种发布/订阅的机制在生活中用的也挺多的。比如我晚上定个8点的闹钟,就相当于订阅了一个事件,这个事件就是’8点钟的闹钟’。当第二天8点闹钟响的时候,就相当于发送了事件。我就会收到这个事件,然后执行相应的动作,比如起床。

那么,既然是订阅/发布。肯定就会有订阅,有发布了。这篇文章先根据源码来研究下订阅事件的流程和原理。

一般我们都会像这样EventBus.getDefault().register(this)使用EventBus。这里首先会创建一个默认的EventBus对象,然后调用EventBus的register(Object subscriber)方法将当前类作为订阅者进行事件的订阅。

/** 
Convenience singleton for apps using a process-wide EventBus instance. 
*/
public static EventBus getDefault() {
      if (defaultInstance == null) {
            synchronized (EventBus.class) {
                if (defaultInstance == null) {
                    defaultInstance = new EventBus();
                }
            }
        }
        return defaultInstance;
}

可以看到,源码是通过单例模式来维护一个EventBus的实例的。从注释中看以看到,这个实例的作用于为整个进程。

这里的关键点就是register(Object subscriber)方法了,上源码:

public void register(Object subscriber) {
    register(subscriber, false, 0);
}

private synchronized void register(Object subscriber, boolean sticky, int priority) {
    List<SubscriberMethod> subscriberMethods = subscriberMethodFinder.findSubscriberMethods(subscriber.getClass());
    for (SubscriberMethod subscriberMethod : subscriberMethods) {
        //遍历所有订阅方法进行订阅
        subscribe(subscriber, subscriberMethod, sticky, priority);
    }
}

可以看到register(Object subscriber)方法内部调用了重载的方法register(Object subscriber, boolean sticky, int priority),所有的逻辑都在这个重载的方法内部实现。

首先执行subscriberMethodFinder.findSubscriberMethods(subscriber.getClass()),我们跟踪到findSubscriberMethods(Class<?> subscriberClass)方法内部去看看。

//寻找subscriberClass中所有的订阅方法
List<SubscriberMethod> findSubscriberMethods(Class<?> subscriberClass) {
    String key = subscriberClass.getName();
    List<SubscriberMethod> subscriberMethods;
    synchronized (methodCache) {//先从缓存中根据类名找对应的方法,如果没找到说明这个类之前没有注册过
        subscriberMethods = methodCache.get(key);
    }
    if (subscriberMethods != null) {//如果找到了则直接返回,不用再查找了
        return subscriberMethods;
    }
    subscriberMethods = new ArrayList<SubscriberMethod>();
    Class<?> clazz = subscriberClass;
    HashMap<String, Class> eventTypesFound = new HashMap<String, Class>();
    StringBuilder methodKeyBuilder = new StringBuilder();
    while (clazz != null) {
        String name = clazz.getName();
        if (name.startsWith("java.") || name.startsWith("javax.") || name.startsWith("android.")) {//排除系统的一些类
    // Skip system classes, this just degrades performance
        break;
}

    // Starting with EventBus 2.2 we enforced methods to be public (might change with annotations again)
        try {
        // This is faster than getMethods, especially when subscribers a fat classes like Activities
                Method[] methods = clazz.getDeclaredMethods();//拿到订阅者中声明的所有方法
                filterSubscriberMethods(subscriberMethods, eventTypesFound, methodKeyBuilder, methods);//对方法进行过滤,找到符合订阅规则的订阅方法
            } catch (Throwable th) {
                th.printStackTrace();
                // Workaround for java.lang.NoClassDefFoundError, see https://github.com/greenrobot/EventBus/issues/149
                Method[] methods = subscriberClass.getMethods();
                subscriberMethods.clear();
                eventTypesFound.clear();
                filterSubscriberMethods(subscriberMethods, eventTypesFound, methodKeyBuilder, methods);
                break;
            }
            clazz = clazz.getSuperclass();
        }
        if (subscriberMethods.isEmpty()) {//查找完后如果没找到,报异常:该类订阅了EventBus,但没有对应的订阅方法
            throw new EventBusException("Subscriber " + subscriberClass + " has no public methods called "
                    + ON_EVENT_METHOD_NAME);
        } else {
            synchronized (methodCache) {//如果找到了,则存起来。防止重复查找。这里的key就是方法名
                methodCache.put(key, subscriberMethods);
            }
            return subscriberMethods;
        }
    }
//对方法进行过滤,筛选出符合事件接收的方法 形如 "public void onEvent**(Object event)"
private void filterSubscriberMethods(List<SubscriberMethod> subscriberMethods,
                                         HashMap<String, Class> eventTypesFound, StringBuilder methodKeyBuilder,
                                         Method[] methods) {
    for (Method method : methods) {
        String methodName = method.getName();
        if (methodName.startsWith(ON_EVENT_METHOD_NAME)) {//方法以onEvent开头
            int modifiers = method.getModifiers(); //方法的限定符类型 public  private等
            Class<?> methodClass = method.getDeclaringClass();//声明方法的类
            if ((modifiers & Modifier.PUBLIC) != 0 && (modifiers & MODIFIERS_IGNORE) == 0) {
                //方法是public的 且非abstract static (bridge synthetic 这两个啥意思?)
                Class<?>[] parameterTypes = method.getParameterTypes(); //方法参数类型
                if (parameterTypes.length == 1) {//方法只有一个参数
                    ThreadMode threadMode = getThreadMode(methodClass, method, methodName);
                    if (threadMode == null) {//不是订阅方法,继续迭代下一个方法
                        continue;
                    }
                    Class<?> eventType = parameterTypes[0];
                    methodKeyBuilder.setLength(0);
                    methodKeyBuilder.append(methodName);
                    methodKeyBuilder.append('>').append(eventType.getName());
                    String methodKey = methodKeyBuilder.toString();
                    Class methodClassOld = eventTypesFound.put(methodKey, methodClass);//以方法名和event类型名作为key
                                                                                        //存入订阅方法列表中
                    if (methodClassOld == null || methodClassOld.isAssignableFrom(methodClass)) {
                        // Only add if not already found in a sub class
                        subscriberMethods.add(new SubscriberMethod(method, threadMode, eventType));
                    } else {
                        // Revert the put, old class is further down the class hierarchy
                        eventTypesFound.put(methodKey, methodClassOld);
                    }
                }
            } else if (!skipMethodVerificationForClasses.containsKey(methodClass)) {
                Log.d(EventBus.TAG, "Skipping method (not public, static or abstract): " + methodClass + "."
                        + methodName);
            }
        }
    }
}

//判断订阅方法需要在哪个线程中调用
private ThreadMode getThreadMode(Class<?> clazz, Method method, String methodName) {
    String modifierString = methodName.substring(ON_EVENT_METHOD_NAME.length());//截取方法名中onEvent后的字符
                                                                                    //如 onEventMainThread -> MainThread
    ThreadMode threadMode;
    if (modifierString.length() == 0) { //方法名为onEVent 默认在PostThread中调用
        threadMode = ThreadMode.PostThread;
    } else if (modifierString.equals("MainThread")) {//方法名为onEVentMainThread 在MainThread中调用
        threadMode = ThreadMode.MainThread;
    } else if (modifierString.equals("BackgroundThread")) {//方法名为onEVentBackgroundThread 在BackgroundThread中调用
        threadMode = ThreadMode.BackgroundThread;
    } else if (modifierString.equals("Async")) {//方法名为onEVentAsync 异步调用
        threadMode = ThreadMode.Async;
    } else {//如果不是以上几个选择 认为不是订阅方法 threadMode为null
        if (!skipMethodVerificationForClasses.containsKey(clazz)) {
                throw new EventBusException("Illegal onEvent method, check for typos: " + method);
        } else {
            threadMode = null;
        }
    }
    return threadMode;
}

上面代码比较长,注释的已经很清楚了。findSubscriberMethods(Class<?> subscriberClass)执行完之后,就找到了订阅者类中所有的能够接受事件的方法并存了起来。

我们继续回到register(Object subscriber, boolean sticky, int priority)方法中,查询完订阅方法后,对所有的方法进行迭代,然后调用subscribe(Object subscriber, SubscriberMethod subscriberMethod, boolean sticky, int priority)进行一一注册。

// Must be called in synchronized block
private void subscribe(Object subscriber, SubscriberMethod subscriberMethod, boolean sticky, int priority) {
    Class<?> eventType = subscriberMethod.eventType;
    //根据事件类型取出该类事件的所有订阅者
    CopyOnWriteArrayList<Subscription> subscriptions = subscriptionsByEventType.get(eventType);
    Subscription newSubscription = new Subscription(subscriber, subscriberMethod, priority);
    if (subscriptions == null) {//如果没有该类型的订阅者 创建一个 加入列表
        subscriptions = new CopyOnWriteArrayList<Subscription>();
        subscriptionsByEventType.put(eventType, subscriptions);
    } else {
        if (subscriptions.contains(newSubscription)) {//如果所有订阅者中已经包含新加入的这个订阅者,提示已经订阅了
            throw new EventBusException("Subscriber " + subscriber.getClass() + " already registered to event "
                    + eventType);
        }
    }

    // Starting with EventBus 2.2 we enforced methods to be public (might change with annotations again)
    // subscriberMethod.method.setAccessible(true);

    int size = subscriptions.size();
    //遍历所有的订阅者 按照优先级(Subscription的priority属性)进行排序
    for (int i = 0; i <= size; i++) {
        if (i == size || newSubscription.priority > subscriptions.get(i).priority) {
            subscriptions.add(i, newSubscription);
            break;
        }
    }

    //取出某个订阅者中所有的订阅事件
    List<Class<?>> subscribedEvents = typesBySubscriber.get(subscriber);
    if (subscribedEvents == null) {
        subscribedEvents = new ArrayList<Class<?>>();
        typesBySubscriber.put(subscriber, subscribedEvents);
    }
    subscribedEvents.add(eventType);

    if (sticky) {//这块是对黏性事件的处理。目前项目中还没用过,不太理解这块
        if (eventInheritance) {
            // Existing sticky events of all subclasses of eventType have to be considered.
            // Note: Iterating over all events may be inefficient with lots of sticky events,
            // thus data structure should be changed to allow a more efficient lookup
            // (e.g. an additional map storing sub classes of super classes: Class -> List<Class>).
            Set<Map.Entry<Class<?>, Object>> entries = stickyEvents.entrySet();
            for (Map.Entry<Class<?>, Object> entry : entries) {
                Class<?> candidateEventType = entry.getKey();
                if (eventType.isAssignableFrom(candidateEventType)) {
                    Object stickyEvent = entry.getValue();
                    checkPostStickyEventToSubscription(newSubscription, stickyEvent);
                }
            }
        } else {
            Object stickyEvent = stickyEvents.get(eventType);
            checkPostStickyEventToSubscription(newSubscription, stickyEvent);
        }
    }
}

subscribe(Object subscriber, SubscriberMethod subscriberMethod, boolean sticky, int priority)方法执行完之后,所有的订阅者以及订阅者中的事件接受方法就被存起来了。

//按事件类型存放的订阅者集合
private final Map<Class<?>, CopyOnWriteArrayList<Subscription>> subscriptionsByEventType;
//按订阅者类型存放的事件集合
private final Map<Object, List<Class<?>>> typesBySubscriber;
//黏性事件(这个怎么理解?)
private final Map<Class<?>, Object> stickyEvents;

至此,事件的订阅就完成了。最终的结果就是EventBus保存了订阅者中所有符合规则(即public void onEvent***(Object event))的能接收事件的方法。当post事件的时候,就会拿着这个事件去保存的订阅者方法中去对比寻找,找到后通过反射去调用对应的方法。

    原文作者:DamonZh
    原文地址: https://www.jianshu.com/p/d3c9133db90b
    本文转自网络文章,转载此文章仅为分享知识,如有侵权,请联系博主进行删除。
点赞