前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >【EventBus】EventBus 源码解析 ( 注册订阅者 | 注册订阅方法详细过程 )

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

作者头像
韩曙亮
发布2023-03-29 17:45:03
3720
发布2023-03-29 17:45:03
举报

文章目录

前言

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

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

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

一、EventBus 注册订阅者


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

代码语言:javascript
复制
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 消息类型 ;

代码语言:javascript
复制
subscribe(Object subscriber, SubscriberMethod subscriberMethod)

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

代码语言:javascript
复制
Subscription newSubscription = new Subscription(subscriber, subscriberMethod);

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

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

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

代码语言:javascript
复制
CopyOnWriteArrayList<Subscription> subscriptions = subscriptionsByEventType.get(eventType);

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

代码语言:javascript
复制
        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);
            }
        }

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

代码语言:javascript
复制
        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 成员变量 添加 订阅者对象 - 订阅方法参数类型集合 键值对 , 该成员变量的作用是取消注册时查找相关的订阅方法 ;

代码语言:javascript
复制
		// 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);

注册订阅方法代码示例 :

代码语言:javascript
复制
/**
 * 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 类中封装了一个订阅者对象和一个订阅方法

代码语言:javascript
复制
final class Subscription {
    final Object subscriber;
    final SubscriberMethod subscriberMethod;
}
本文参与 腾讯云自媒体分享计划,分享自作者个人站点/博客。
原始发表:2021-09-24,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 作者个人站点/博客 前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体分享计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • 文章目录
  • 前言
  • 一、EventBus 注册订阅者
  • 二、注册订阅方法的具体过程
  • 三、Subscription 类
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档