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

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

文章目录

一、EventBus 注册订阅者

二、订阅方法

三、查找订阅方法 findSubscriberMethods 方法

四、查找订阅方法 findUsingInfo 方法

五、查找订阅方法 findUsingReflectionInSingleClass





一、EventBus 注册订阅者


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


① 获取 订阅者 集合 , 查找当前订阅类中符合条件的订阅方法集合 ;

② 遍历 订阅者 集合 , 进行事件订阅 , 保存数据 , 这些数据就是一些映射关系

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




二、订阅方法


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






三、查找订阅方法 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;
        }
    }
}


下面分析 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);
    }
}





五、查找订阅方法 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()));
                        }
                    }
                } 
            } 
        }
    }
}
目录
相关文章
|
9月前
|
算法 测试技术 C语言
深入理解HTTP/2:nghttp2库源码解析及客户端实现示例
通过解析nghttp2库的源码和实现一个简单的HTTP/2客户端示例,本文详细介绍了HTTP/2的关键特性和nghttp2的核心实现。了解这些内容可以帮助开发者更好地理解HTTP/2协议,提高Web应用的性能和用户体验。对于实际开发中的应用,可以根据需要进一步优化和扩展代码,以满足具体需求。
906 29
|
9月前
|
JSON 监控 网络协议
Bilibili直播信息流:连接方法与数据解析
本文详细介绍了自行实现B站直播WebSocket连接的完整流程。解析了基于WebSocket的应用层协议结构,涵盖认证包构建、心跳机制维护及数据包解析步骤,为开发者定制直播数据监控提供了完整技术方案。
|
9月前
|
前端开发 数据安全/隐私保护 CDN
二次元聚合短视频解析去水印系统源码
二次元聚合短视频解析去水印系统源码
383 4
|
9月前
|
安全 IDE Java
重学Java基础篇—Java Object类常用方法深度解析
Java中,Object类作为所有类的超类,提供了多个核心方法以支持对象的基本行为。其中,`toString()`用于对象的字符串表示,重写时应包含关键信息;`equals()`与`hashCode()`需成对重写,确保对象等价判断的一致性;`getClass()`用于运行时类型识别;`clone()`实现对象复制,需区分浅拷贝与深拷贝;`wait()/notify()`支持线程协作。此外,`finalize()`已过时,建议使用更安全的资源管理方式。合理运用这些方法,并遵循最佳实践,可提升代码质量与健壮性。
276 1
|
9月前
|
JavaScript 算法 前端开发
JS数组操作方法全景图,全网最全构建完整知识网络!js数组操作方法全集(实现筛选转换、随机排序洗牌算法、复杂数据处理统计等情景详解,附大量源码和易错点解析)
这些方法提供了对数组的全面操作,包括搜索、遍历、转换和聚合等。通过分为原地操作方法、非原地操作方法和其他方法便于您理解和记忆,并熟悉他们各自的使用方法与使用范围。详细的案例与进阶使用,方便您理解数组操作的底层原理。链式调用的几个案例,让您玩转数组操作。 只有锻炼思维才能可持续地解决问题,只有思维才是真正值得学习和分享的核心要素。如果这篇博客能给您带来一点帮助,麻烦您点个赞支持一下,还可以收藏起来以备不时之需,有疑问和错误欢迎在评论区指出~
|
9月前
|
存储 前端开发 JavaScript
在线教育网课系统源码开发指南:功能设计与技术实现深度解析
在线教育网课系统是近年来发展迅猛的教育形式的核心载体,具备用户管理、课程管理、教学互动、学习评估等功能。本文从功能和技术两方面解析其源码开发,涵盖前端(HTML5、CSS3、JavaScript等)、后端(Java、Python等)、流媒体及云计算技术,并强调安全性、稳定性和用户体验的重要性。
|
9月前
|
负载均衡 JavaScript 前端开发
分片上传技术全解析:原理、优势与应用(含简单实现源码)
分片上传通过将大文件分割成多个小的片段或块,然后并行或顺序地上传这些片段,从而提高上传效率和可靠性,特别适用于大文件的上传场景,尤其是在网络环境不佳时,分片上传能有效提高上传体验。 博客不应该只有代码和解决方案,重点应该在于给出解决方案的同时分享思维模式,只有思维才能可持续地解决问题,只有思维才是真正值得学习和分享的核心要素。如果这篇博客能给您带来一点帮助,麻烦您点个赞支持一下,还可以收藏起来以备不时之需,有疑问和错误欢迎在评论区指出~
|
监控 Java 应用服务中间件
高级java面试---spring.factories文件的解析源码API机制
【11月更文挑战第20天】Spring Boot是一个用于快速构建基于Spring框架的应用程序的开源框架。它通过自动配置、起步依赖和内嵌服务器等特性,极大地简化了Spring应用的开发和部署过程。本文将深入探讨Spring Boot的背景历史、业务场景、功能点以及底层原理,并通过Java代码手写模拟Spring Boot的启动过程,特别是spring.factories文件的解析源码API机制。
366 2
|
9月前
|
移动开发 前端开发 JavaScript
从入门到精通:H5游戏源码开发技术全解析与未来趋势洞察
H5游戏凭借其跨平台、易传播和开发成本低的优势,近年来发展迅猛。接下来,让我们深入了解 H5 游戏源码开发的技术教程以及未来的发展趋势。
|
10月前
|
机器学习/深度学习 自然语言处理 算法
生成式 AI 大语言模型(LLMs)核心算法及源码解析:预训练篇
生成式 AI 大语言模型(LLMs)核心算法及源码解析:预训练篇
2657 1

推荐镜像

更多
  • DNS