EventBusEventBus 源码解析 ( 注册订阅者 | 注册订阅方法详细过程 )

Posted 韩曙亮

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了EventBusEventBus 源码解析 ( 注册订阅者 | 注册订阅方法详细过程 )相关的知识,希望对你有一定的参考价值。

前言

在上一篇博客 【EventBus】EventBus 源码解析 ( 注册订阅者 | 订阅方法 | 查找订阅方法 ) 中 , 介绍了注册订阅者的第一个步骤 , 查找订阅者 ;

首先要获取当前的 List<SubscriberMethod> subscriberMethods 订阅方法集合 , 该集合从 SubscriberMethodFinder 的缓存 Map<Class<?>, List<SubscriberMethod>> METHOD_CACHE 获取 , 该缓存是在第一次获取订阅方法时生成 , 之后获取都直接从缓存中获取即可 ;

METHOD_CACHE 缓存生成策略 : 如果是第一次获取订阅方法 , 缓存是空的 , 此时通过反射获取该 订阅类 所有符合条件的订阅方法 , 将 订阅方法 封装到 SubscriberMethod 中 , 然后添加到 findState.subscriberMethods 集合中 ;





一、EventBus 注册订阅者



获取到订阅方法集合后 , 然后开始遍历订阅方法集合 , 调用 subscribe 方法 , 注册订阅者 ;

public class EventBus {
    /**
     * 注册给定订阅服务器以接收事件。订阅者一旦对接收事件不再感兴趣,就必须调用{@link#unregister(Object)}。
     * <p/>
     * 订阅服务器具有必须由{@link Subscribe}注释的事件处理方法。
     * {@link Subscribe}注释还允许类似{@link ThreadMode}和优先级的配置。
     */
    public void register(Object subscriber) {
        Class<?> subscriberClass = subscriber.getClass();
        // 1. 获取 订阅者 集合 , 查找当前订阅类中符合条件的订阅方法集合 
        List<SubscriberMethod> subscriberMethods = subscriberMethodFinder.findSubscriberMethods(subscriberClass);
        // 2. 遍历 订阅者 集合 , 进行事件订阅 , 保存数据 , 这些数据就是一些映射关系 
        synchronized (this) {
            for (SubscriberMethod subscriberMethod : subscriberMethods) {
                subscribe(subscriber, subscriberMethod);
            }
        }
    }
}




二、注册订阅方法的具体过程



获取订阅方法参数类型 , 可以是任意类型 , 自定义的 MessageEvent 消息类型 ;

subscribe(Object subscriber, SubscriberMethod subscriberMethod)

封装 Subscription 对象 , Subscription 中封装了一个订阅者对象和一个订阅方法 ;

Subscription newSubscription = new Subscription(subscriber, subscriberMethod);

Map<Class<?>, CopyOnWriteArrayList<Subscription>> subscriptionsByEventType 成员变量中获取 Subscription 集合 , 该 HashMap 的用途是保存当前的 eventType 对应的所有的订阅类和订阅方法 , 以便消息中心获取对应类型的消息后 , 可以顺利将其传递给相应订阅方法 ;

  • Key 是事件类型对象 ;
  • Value 是 Subscription 集合 , Subscription 中封装了一个订阅者对象和一个订阅方法

CopyOnWriteArrayList 是线程安全集合 , 写入数据的时候会拷贝一份出来 , 不会影响其它线程读取该集合的操作

CopyOnWriteArrayList<Subscription> subscriptions = subscriptionsByEventType.get(eventType);

如果上述集合中没有保存本次遍历的数据 , 则将订阅方法信息保存到上述集合中 ;

        if (subscriptions == null) {
            subscriptions = new CopyOnWriteArrayList<>();
            // 保存键值对
            subscriptionsByEventType.put(eventType, subscriptions);
        } else {
            if (subscriptions.contains(newSubscription)) {
                throw new EventBusException("Subscriber " + subscriber.getClass() + " already registered to event "
                        + eventType);
            }
        }

如果订阅方法设置了优先级注解属性 , 则处理订阅方法优先级 , 重新进行排列 ;

        int size = subscriptions.size();
        for (int i = 0; i <= size; i++) {
        	// 处理订阅方法优先级 , 重新进行排列  
            if (i == size || subscriberMethod.priority > subscriptions.get(i).subscriberMethod.priority) {
                subscriptions.add(i, newSubscription);
                break;
            }
        }

Map<Object, List<Class<?>>> typesBySubscriber 成员变量 添加 订阅者对象 - 订阅方法参数类型集合 键值对 , 该成员变量的作用是取消注册时查找相关的订阅方法 ;

		// Map<Object, List<Class<?>>> typesBySubscriber
		//		Key : 订阅者对象
		//		Value : 订阅方法参数类型集合 
		//		取消注册时 , 需要使用该方法 
        List<Class<?>> subscribedEvents = typesBySubscriber.get(subscriber);
        if (subscribedEvents == null) {
            subscribedEvents = new ArrayList<>();
            typesBySubscriber.put(subscriber, subscribedEvents);
        }
        subscribedEvents.add(eventType);

注册订阅方法代码示例 :

/**
 * EventBus是Java和android的中央发布/订阅事件系统。
 * 事件被发布({@link#post(Object)})到总线,总线将其传递给具有匹配处理程序的订阅者
 * 事件类型的方法。
 * 要接收事件,订阅者必须使用{@link#register(Object)}将自己注册到总线。
 * 一旦注册,订阅服务器将接收事件,直到调用{@link#unregister(Object)}。
 * 事件处理方法必须由{@link Subscribe}注释,必须是公共的,不返回任何内容(void),
 * 并且只有一个参数(事件)。
 */
public class EventBus {
    // 该方法必须在同步代码块中调用 
    private void subscribe(Object subscriber, SubscriberMethod subscriberMethod) {
    	// 获取订阅方法参数类型 , 可以是任意类型 , 自定义的 MessageEvent 消息类型
        Class<?> eventType = subscriberMethod.eventType;
        // Subscription 中封装了一个订阅者对象和一个订阅方法 
        Subscription newSubscription = new Subscription(subscriber, subscriberMethod);
        
        // Map<Class<?>, CopyOnWriteArrayList<Subscription>> subscriptionsByEventType 成员变量 
        //		Key 是事件类型对象 
        //		Value 是 Subscription 集合 , Subscription 中封装了一个订阅者对象和一个订阅方法 
        //		该 HashMap 的用途是保存当前的 eventType 对应的所有的订阅类和订阅方法 
        //		以便消息中心获取对应类型的消息后 , 可以顺利将其传递给相应订阅方法 
        // CopyOnWriteArrayList 是线程安全集合 , 写入数据的时候会拷贝一份出来 , 不会影响其它线程读取该集合的操作
        CopyOnWriteArrayList<Subscription> subscriptions = subscriptionsByEventType.get(eventType);
        
        // 如果上述集合中没有保存本次遍历的数据 , 则将订阅方法信息保存到上述集合中 
        if (subscriptions == null) {
            subscriptions = new CopyOnWriteArrayList<>();
            // 保存键值对
            subscriptionsByEventType.put(eventType, subscriptions);
        } else {
            if (subscriptions.contains(newSubscription)) {
                throw new EventBusException("Subscriber " + subscriber.getClass() + " already registered to event "
                        + eventType);
            }
        }

        int size = subscriptions.size();
        for (int i = 0; i <= size; i++) {
        	// 处理订阅方法优先级 , 重新进行排列  
            if (i == size || subscriberMethod.priority > subscriptions.get(i).subscriberMethod.priority) {
                subscriptions.add(i, newSubscription);
                break;
            }
        }

		// Map<Object, List<Class<?>>> typesBySubscriber
		//		Key : 订阅者对象
		//		Value : 订阅方法参数类型集合 
		//		取消注册时 , 需要使用该方法 
        List<Class<?>> subscribedEvents = typesBySubscriber.get(subscriber);
        if (subscribedEvents == null) {
            subscribedEvents = new ArrayList<>();
            typesBySubscriber.put(subscriber, subscribedEvents);
        }
        subscribedEvents.add(eventType);

        // 处理方法粘性相关业务逻辑
        if (subscriberMethod.sticky) {
            if (eventInheritance) {
                // 必须考虑eventType的所有子类的现有粘性事件。
                // 注意:迭代所有事件可能效率低下,因为有很多粘性事件,
                // 因此,应更改数据结构以允许更高效的查找
                // 例如,存储超类子类的附加映射: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);
            }
        }
    }
}




三、Subscription 类



Subscription 类中封装了一个订阅者对象和一个订阅方法

final class Subscription {
    final Object subscriber;
    final SubscriberMethod subscriberMethod;
}

以上是关于EventBusEventBus 源码解析 ( 注册订阅者 | 注册订阅方法详细过程 )的主要内容,如果未能解决你的问题,请参考以下文章

EventBusEventBus 源码解析 ( 注册订阅者 | 订阅方法 | 查找订阅方法 )

EventBusEventBus 源码解析 ( 事件发送 | 线程池中执行订阅方法 )

EventBusEventBus 源码解析 ( 事件发送 | postToSubscription 方法 | EventBus 线程模式处理细节 )

EventBusEventBus 源码解析 ( 事件发送 | EventBus.post 方法 | EventBus.postSingleEvent 方法 )

EventBusEventBus 源码解析 ( 注册订阅者总结 | 从封装的数据结构角度分析 EventBus )

EventBusEventBus 源码解析 ( 事件发送 | 发布线程为 子线程 切换到 主线程 执行订阅方法的过程分析 )