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

举报
韩曙亮 发表于 2022/01/13 22:29:11 2022/01/13
【摘要】 文章目录 一、EventBus 注册订阅者二、订阅方法三、查找订阅方法 findSubscriberMethods 方法四、查找订阅方法 findUsingInfo 方法五、查找订阅方法 find...





一、EventBus 注册订阅者



EventBus 中调用 EventBus.getDefault().register(this) 注册订阅者 ; 该方法中主要进行了如下 2 2 2 个步骤 :

  • ① 获取 订阅者 集合 , 查找当前订阅类中符合条件的订阅方法集合 ;
  • ② 遍历 订阅者 集合 , 进行事件订阅 , 保存数据 , 这些数据就是一些映射关系
/**
 * EventBus是Java和Android的中央发布/订阅事件系统。
 * 事件被发布({@link#post(Object)})到总线,总线将其传递给具有匹配处理程序的订阅者
 * 事件类型的方法。
 * 要接收事件,订阅者必须使用{@link#register(Object)}将自己注册到总线。
 * 一旦注册,订阅服务器将接收事件,直到调用{@link#unregister(Object)}。
 * 事件处理方法必须由{@link Subscribe}注释,必须是公共的,不返回任何内容(void),
 * 并且只有一个参数(事件)。
 */
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
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28




二、订阅方法



SubscriberMethod 对订阅的方法进行了一些封装 , 包括了方法的 Method 对象 Method method , 线程模式 ThreadMode threadMode , 事件类型 Class<?> eventType, 优先级 int priority , 粘性 boolean sticky 等 ;

/** 由EventBus内部使用并生成订户索引。 */
public class SubscriberMethod {
    final Method method;
    final ThreadMode threadMode;
    final Class<?> eventType;
    final int priority;
    final boolean sticky;
    /** Used for efficient comparison */
    String methodString;
}

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




三、查找订阅方法 findSubscriberMethods 方法



订阅方法缓存机制 : 从缓存中获取 订阅方法 , METHOD_CACHE 缓存是一个 HashMap 集合 ;

如果订阅者有很多方法 , 如果每次订阅都要查询所有的方法 , 如果遍历一次 Activity 的所有方法 很消耗性能 ; 因此这里引入了缓存机制 ;

第一次订阅时 , 将方法都放在缓存集合中 , 如果第二次订阅 , 不用再次查找方法 ;


如果缓存中没有订阅方法 , 那么说明这是第一次查找订阅方法 , 一般情况下都是调用 subscriberMethods = findUsingInfo(subscriberClass) 方法 , 获取订阅方法 ;

class SubscriberMethodFinder {
	// 方法缓存集合
    private static final Map<Class<?>, List<SubscriberMethod>> METHOD_CACHE = new ConcurrentHashMap<>();
    List<SubscriberMethod> findSubscriberMethods(Class<?> subscriberClass) {
    	// 从缓存中获取 订阅方法 , METHOD_CACHE 缓存是一个 HashMap 集合
    	//		如果订阅者有很多方法 , 如果每次订阅都要查询所有的方法 , 很消耗性能 
    	//		第一次订阅时 , 将方法都放在集合中 
    	//		如果第二次订阅 , 不用再次查找方法
        List<SubscriberMethod> subscriberMethods = METHOD_CACHE.get(subscriberClass);
        if (subscriberMethods != null) {
            return subscriberMethods;
        }

		// 如果缓存中没有订阅方法 , 那么说明这是第一次查找订阅方法
		// ignoreGeneratedIndex 属性默认是 false 
		//		是否忽略注解生成器 
        if (ignoreGeneratedIndex) {
            subscriberMethods = findUsingReflection(subscriberClass);
        } else {
        	// 一般情况下 , 调用的是该方法  
            subscriberMethods = findUsingInfo(subscriberClass);
        }
        if (subscriberMethods.isEmpty()) {
            throw new EventBusException("Subscriber " + subscriberClass
                    + " and its super classes have no public methods with the @Subscribe annotation");
        } else {
            METHOD_CACHE.put(subscriberClass, subscriberMethods);
            return subscriberMethods;
        }
    }
}

  
 
  • 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

下面分析 findUsingInfo 方法 ;





四、查找订阅方法 findUsingInfo 方法



FindState 是辅助类 , 其中进行了状态保存等信息 ;

参数 Class<?> subscriberClass 是订阅者类 , 将订阅类设置到 FindState 查找状态类对象中 ;

该方法的核心是调用了 findUsingReflectionInSingleClass(findState) 方法 , 进行后续查找操作 ;

class SubscriberMethodFinder {
    private List<SubscriberMethod> findUsingInfo(Class<?> subscriberClass) {
    	// FindState 是辅助类 , 其中进行了状态保存等信息 
        FindState findState = prepareFindState();
        // 将订阅类设置到 FindState 查找状态类对象中 
        findState.initForSubscriber(subscriberClass);
        // 订阅类的字节码类
        while (findState.clazz != null) {
            findState.subscriberInfo = getSubscriberInfo(findState);
            // 从 查找状态 中 获取订阅信息 , 如果订阅信息不为空 , 进行如下处理 
            if (findState.subscriberInfo != null) {
                SubscriberMethod[] array = findState.subscriberInfo.getSubscriberMethods();
                for (SubscriberMethod subscriberMethod : array) {
                    if (findState.checkAdd(subscriberMethod.method, subscriberMethod.eventType)) {
                        findState.subscriberMethods.add(subscriberMethod);
                    }
                }
            } else { // 从 查找状态 中 获取订阅信息 , 如果订阅信息为空 , 进行如下处理 
                findUsingReflectionInSingleClass(findState);
            }
            // 查找订阅类的上级父类 , 继续进行循环 
            findState.moveToSuperclass();
        }
        return getMethodsAndRelease(findState);
    }
}

  
 
  • 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




五、查找订阅方法 findUsingReflectionInSingleClass



通过反射获取订阅者类中的所有方法 , 遍历 订阅者 类中的所有方法 , 过滤掉不符合条件的方法 , 将符合条件的方法封装到 findState.subscriberMethods 集合中 ;

过滤方案 :

  • 订阅方法的参数个数肯定只有 1 个 ;
  • 订阅方法上有 @Subscribe 注解 ;
  • @Subscribe 注解属性判断 ;
  • @Subscribe 注解线程模式判断 ;
class SubscriberMethodFinder {
    private void findUsingReflectionInSingleClass(FindState findState) {
        Method[] methods;
        try {
        	// 通过反射获取订阅者类中的所有方法 
            // 这比getMethods快,特别是当订阅者是像 Activity 这样的大类时
            methods = findState.clazz.getDeclaredMethods();
        } catch (Throwable th) {
        }
        // 遍历 订阅者 类中的所有方法 , 过滤掉不符合条件的方法 , 
        for (Method method : methods) {
            int modifiers = method.getModifiers();
            if ((modifiers & Modifier.PUBLIC) != 0 && (modifiers & MODIFIERS_IGNORE) == 0) {
                Class<?>[] parameterTypes = method.getParameterTypes();
                // 订阅方法的参数个数肯定只有 1 个
                if (parameterTypes.length == 1) {
                	// 订阅方法上有 @Subscribe 注解 
                    Subscribe subscribeAnnotation = method.getAnnotation(Subscribe.class);
                    if (subscribeAnnotation != null) {
                    	// @Subscribe 注解属性判断 
                        Class<?> eventType = parameterTypes[0];
                        if (findState.checkAdd(method, eventType)) {
                        	// @Subscribe 注解线程模式判断
                            ThreadMode threadMode = subscribeAnnotation.threadMode();
                            // 将符合条件的方法封装到 findState.subscriberMethods 集合中 ; 
                            findState.subscriberMethods.add(new SubscriberMethod(method, eventType, threadMode,
                                    subscribeAnnotation.priority(), subscribeAnnotation.sticky()));
                        }
                    }
                } 
            } 
        }
    }
}

  
 
  • 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

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

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

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

评论(0

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

全部回复

上滑加载中

设置昵称

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

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

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