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

举报
韩曙亮 发表于 2022/01/13 01:13:12 2022/01/13
【摘要】 文章目录 前言一、EventBus 注册订阅者二、注册订阅方法的具体过程三、Subscription 类 前言 在上一篇博客 【EventBus】EventBus 源码解析 ( 注册订阅者...

前言

在上一篇博客 【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);
            }
        }
    }
}

  
 
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19




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



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

subscribe(Object subscriber, SubscriberMethod subscriberMethod)

  
 
  • 1

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

Subscription newSubscription = new Subscription(subscriber, subscriberMethod);

  
 
  • 1

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

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

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

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

  
 
  • 1

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

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

  
 
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10

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

        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;
            }
        }

  
 
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8

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

  
 
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10

注册订阅方法代码示例 :

/**
 * 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);
            }
        }
    }
}

  
 
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43
  • 44
  • 45
  • 46
  • 47
  • 48
  • 49
  • 50
  • 51
  • 52
  • 53
  • 54
  • 55
  • 56
  • 57
  • 58
  • 59
  • 60
  • 61
  • 62
  • 63
  • 64
  • 65
  • 66
  • 67
  • 68
  • 69
  • 70
  • 71
  • 72
  • 73
  • 74
  • 75
  • 76
  • 77
  • 78
  • 79




三、Subscription 类



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

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

  
 
  • 1
  • 2
  • 3
  • 4

文章来源: hanshuliang.blog.csdn.net,作者:韩曙亮,版权归原作者所有,如需转载,请联系作者。

原文链接:hanshuliang.blog.csdn.net/article/details/120444960

【版权声明】本文为华为云社区用户转载文章,如果您发现本社区中有涉嫌抄袭的内容,欢迎发送邮件进行举报,并提供相关证据,一经查实,本社区将立刻删除涉嫌侵权内容,举报邮箱: cloudbbs@huaweicloud.com
  • 点赞
  • 收藏
  • 关注作者

评论(0

0/1000
抱歉,系统识别当前为高风险访问,暂不支持该操作

全部回复

上滑加载中

设置昵称

在此一键设置昵称,即可参与社区互动!

*长度不超过10个汉字或20个英文字符,设置后3个月内不可修改。

*长度不超过10个汉字或20个英文字符,设置后3个月内不可修改。