EventBus源码解读详细注释(1)register的幕后黑手

2023-12-04 04:08

本文主要是介绍EventBus源码解读详细注释(1)register的幕后黑手,希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!

[EventBus源码分析(一):入口函数提纲挈领(2.4版本)](http://blog.csdn.net/wangshihui512/article/details/51802172)
[EventBus源码分析(二):register方法保存事件的订阅者列表(2.4版本)](http://blog.csdn.net/wangshihui512/article/details/51819508)
[EventBus源码分析(三):post方法发布事件【获取事件的所有订阅者,反射调用订阅者事件处理方法】(2.4版本)](http://blog.csdn.net/wangshihui512/article/details/51821143)
[EventBus源码分析(四):线程模型分析(2.4版本)](http://blog.csdn.net/wangshihui512/article/details/51832001)
[EventBus源码解读详细注释(1)register的幕后黑手](http://blog.csdn.net/wangshihui512/article/details/50914817)
[EventBus源码解读详细注释(2)MainThread线程模型分析](http://blog.csdn.net/wangshihui512/article/details/50934012)
[EventBus源码解读详细注释(3)PostThread、MainThread、BackgroundThread、Async四种线程模式的区别](http://blog.csdn.net/wangshihui512/article/details/50935729)
[EventBus源码解读详细注释(4)register时刷新的两个map](http://blog.csdn.net/wangshihui512/article/details/50938663)
[EventBus源码解读详细注释(5)事件消息继承性分析 eventInheritance含义](http://blog.csdn.net/wangshihui512/article/details/50947102)
[EventBus源码解读详细注释(6)从事件发布到事件处理,究竟发生了什么!](http://blog.csdn.net/wangshihui512/article/details/50949960)



register(this)就是去当前类,遍历所有的方法,按照事件处理方法的命名规则约束(onEvent开头,只有一个参数,非static,非abstract的public方法)过滤出事件处理方法然后进行存储。然后循环遍历当前类的父类,做同样的处理。

通过阅读源代码发现如果子类没有覆写父类的事件处理方法,那么父类的事件处理方法将会加入事件处理方法列表。

private synchronized void register(Object subscriber, boolean sticky, int priority) {/*getClass():* Returns the unique instance of  Class that represents this object's class.*/
    /*过滤出订阅者的所有事件处理方法(包括父类的),以列表形式返回*/
    List<SubscriberMethod> subscriberMethods = subscriberMethodFinder.findSubscriberMethods(subscriber.getClass());for (SubscriberMethod subscriberMethod : subscriberMethods) {/*遍历,对订阅者的每一个事件处理方法进行保存/
        subscribe(subscriber, subscriberMethod, sticky, priority);}
}

 
/**
 * @param subscriberClass
 * @return
 */
/*订阅者 subscriberClass:注册EventBus的Activity或其他组件,EventBus.getDefault().register(this)中的this*/
/*方法findSubscriberMethods的作用是过滤出订阅者的所有事件处理方法方法,以列表形式返回*/
/*在EventBus中的观察者通常有四种订阅函数(就是某件事情发生被调用的方法)
1、onEvent
2、onEventMainThread
3、onEventBackground
4、onEventAsync
这四种订阅函数都是使用onEvent开头的,它们的功能稍有不同,在介绍不同之前先介绍两个概念:
告知观察者事件发生时通过EventBus.post函数实现,这个过程叫做事件的发布,观察者被告知事件发生叫做事件的接收,是通过下面的订阅函数实现的。
onEvent:如果使用onEvent作为订阅函数,那么该事件在哪个线程发布出来的,onEvent就会在哪个线程中运行,也就是说发布事件和接收事件线程在同一个线程。
使用这个方法时,在onEvent方法中不能执行耗时操作,如果执行耗时操作容易导致事件分发延迟。
onEventMainThread:如果使用onEventMainThread作为订阅函数,那么不论事件是在哪个线程中发布出来的,都会在主线程中调用事件处理方法,这个在Android中是非常有用的,因为在Android中只能在UI线程中更新UI,所以在onEvnetMainThread方法中是不能执行耗时操作的。
onEvnetBackground:如果使用onEventBackgrond作为订阅函数,那么如果事件是在UI线程中发布出来的,那么onEventBackground就会在子线程中运行,如果事件本来就是子线程中发布出来的,那么onEventBackground函数直接在该子线程中执行。
onEventAsync:使用这个函数作为订阅函数,那么无论事件在哪个线程发布,都会创建新的子线程在执行onEventAsync.*/
List<SubscriberMethod> findSubscriberMethods(Class<?> subscriberClass) {/*getName():Returns the name of the class represented by this Class 返回订阅者的类名,比如xxx.xxx.xxx.MainActivity*/
    String key = subscriberClass.getName();List<SubscriberMethod> subscriberMethods;/* private static final Map<String, List<SubscriberMethod>> methodCache = new HashMap<String, List<SubscriberMethod>>();*/
    /*方法缓存就是一个静态最终的HashMap,key是订阅者的类名,value是订阅者事件处理方法的列表*/
    /*先读缓存,看能否命中*/
    synchronized (methodCache) {subscriberMethods = methodCache.get(key);}/*如果缓存命中,直接返回缓存中的事件处理方法列表*/
    if (subscriberMethods != null) {return subscriberMethods;}/*缓存不命中,开始按照事件处理方法的命名规则进行过滤*/
    subscriberMethods = new ArrayList<SubscriberMethod>();Class<?> clazz = subscriberClass;/*eventTypesFound存放的是事件处理方法的过滤结果,key就是过滤出来的事件处理方法,value就是声明了这个事件处理方法的订阅者的Class*/
    /*因为是循环处理订阅者的所有父类的,所以如果子类覆写了父类的事件处理方法,那么父类和子类的事件处理方法名称和参数完全一致
    * 而事件处理方法名称和参数是作为eventTypeFound的key存储的,key必须唯一*/
    HashMap<String, Class> eventTypesFound = new HashMap<String, Class>();/*注意这个StringBuilder类是在循环外创建的,在循环内创建就太冲动了*/
    StringBuilder methodKeyBuilder = new StringBuilder();/*循环的最后一句话:clazz = clazz.getSuperclass(); 表示不仅要找当前类的事件处理方法,还要找所有父类的事件处理方法*/
    while (clazz != null) {String name = clazz.getName();/*如果类名是以java. javax. android. 为前缀,说明是系统类,不处理系统的类,直接结束循环,这是出于性能的考虑*/
        if (name.startsWith("java.") || name.startsWith("javax.") || name.startsWith("android.")) {// Skip system classes, this just degrades performance
            break;}// Starting with EventBus 2.2 we enforced methods to be public (might change with annotations again)
        /*从EventBus2.2版本开始要求事件处理方法必须是公开的方法*/
        try {// This is faster than getMethods, especially when subscribers a fat classes like Activities
            /*
            getDeclaredMethods():返回所有方法,不仅仅是公开的,但不包括继承的方法
            getMethods():返回所有公开方法,包括继承来的方法,如果订阅者是个比较复杂的类,那么getDeclaredMethods()速度要快于getMethods()
            getDeclaredMethods():Returns an array containing  Method objects
            for all methods declared in the class represented by this Class. */
            Method[] methods = clazz.getDeclaredMethods();/*对订阅者的所有方法进行过滤,得到事件处理方法列表*/
            filterSubscriberMethods(subscriberMethods, eventTypesFound, methodKeyBuilder, methods);} catch (Throwable th) {th.printStackTrace();// Workaround for java.lang.NoClassDefFoundError, see https://github.com/greenrobot/EventBus/issues/149
            Method[] methods = subscriberClass.getMethods();subscriberMethods.clear();eventTypesFound.clear();filterSubscriberMethods(subscriberMethods, eventTypesFound, methodKeyBuilder, methods);break;}/*获取父类,对订阅者的父类进行同样的处理*/
        clazz = clazz.getSuperclass();}/*如果组件注册了EventBus,就成为了订阅者,订阅者不能没有事件处理方法,否则抛出异常*/
    if (subscriberMethods.isEmpty()) {throw new EventBusException("Subscriber " + subscriberClass + " has no public methods called "
                + ON_EVENT_METHOD_NAME);} else {/*过滤一次事件处理方法还是挺麻烦的,所以还是加入缓存吧*/
        synchronized (methodCache) {methodCache.put(key, subscriberMethods);}return subscriberMethods;}
}/**对订阅者的所有方法进行过滤,得到事件处理方法列表
 * @param subscriberMethods 返回的事件处理方法列表
 * @param eventTypesFound  过滤出的时间处理方法
 * @param methodKeyBuilder
 * @param methods 所有方法
 */
private void filterSubscriberMethods(List<SubscriberMethod> subscriberMethods,HashMap<String, Class> eventTypesFound, StringBuilder methodKeyBuilder,Method[] methods) {/*遍历每个方法,对每个方法进行处理,判断是否为事件处理方法*/
    for (Method method : methods) {/*得到方法名*/
        String methodName = method.getName();/*private static final String ON_EVENT_METHOD_NAME = "onEvent";*/
        /*如果方法名以onEvent开头就接着处理*/
        if (methodName.startsWith(ON_EVENT_METHOD_NAME)) {/*获取该方法的修饰符*/
            int modifiers = method.getModifiers();/*获取声明了这个方法的类*/
            /*getDeclaringClass():returns the class that declares this method.*/
            Class<?> methodClass = method.getDeclaringClass();/* private static final int MODIFIERS_IGNORE = Modifier.ABSTRACT | Modifier.STATIC | BRIDGE | SYNTHETIC;*/
            /*如果是非abstract、static的public方法,接着处理*/
            if ((modifiers & Modifier.PUBLIC) != 0 && (modifiers & MODIFIERS_IGNORE) == 0) {/*获取该方法的参数类型列表*/
                Class<?>[] parameterTypes = method.getParameterTypes();/*事件处理方法约定只有一个参数*/
                if (parameterTypes.length == 1) {/*根据方法名判断线程类型*/
                    ThreadMode threadMode = getThreadMode(methodClass, method, methodName);/*如果这个方法不是事件处理方法,跳出这次循环*/
                    if (threadMode == null) {continue;}/*获取参数类型,事件处理函数只有一个参数,所以取参数列表的第一项*/
                    Class<?> eventType = parameterTypes[0];methodKeyBuilder.setLength(0);methodKeyBuilder.append(methodName);methodKeyBuilder.append('>').append(eventType.getName());/*举个例子,如果事件处理函数为 onEvent(AutoRefreshMsg msg)
                    * 那么methodKey就为onEvent>AutoRefreshMsg*/
                    String methodKey = methodKeyBuilder.toString();/*HashMap<String, Class> eventTypesFound 是传入的参数*/
                    /*eventTypesFound 中key为onEvent>AutoRefreshMsg这种格式的字符串,value为声明了该事件处理方法的类*/
                    /* HashMap : public V put(K key, V value)
                     * Maps the specified key to the specified value.
                     * @param key the key.
                     * @param value the value.
                     * @return the value of any previous mapping with the specified key or
                     *         code null if there was no such mapping.
                     *         如果HashMap之前没有这样的K V对,或者说是这样的K V对第一次添加到HashMap,就返回null
                     */
                    /* methodClassOld表示之前是否已经存储过了定义了这个事件处理方法的类,如果没有,那么methodClassOld==null*/
                    /*注意HashMap的put方法的返回值,返回对应k之前存储的value或者null如果HashMap*/
                    /*注意对订阅者事件处理方法的过滤是先过滤当前订阅者,然后循环过滤当前订阅者的父类*/
                    /*所以如果子类覆写了父类的事件处理方法,那么肯定先处理子类
                    * 先把此事件处理方法和子类的类Class存储在eventTypesFound
                    * 然后再下一次循环处理父类的时候,调用map的put方法,因为子类覆写了父类的事件处理方法
                    * 所以key值存在,put方法返回之前对用的value,也就是子类的Class*/
                    Class methodClassOld = eventTypesFound.put(methodKey, methodClass);/* public boolean isAssignableFrom(Class<?> c)
                    isAssignableFrom:Can c  be assigned to this class */
                    /* Class<?> methodClass = method.getDeclaringClass();*/
                    /*注意这里或运算的短路性质*/
                    /* instanceof 针对实例  isAssignableFrom针对class对象*/
                    /* new A() instanceof B 判断A对象是否是B类的子类或B类的实例*/
                    /* B.class.isAssignableFrom(A.class) A类是否可以赋值给B类,也就是判断B是不是A的父类*/
 
                    /*对已事件处理方法,子类和父类的都一样,所以只把最底层的订阅者的事件处理方法加入事件处理方法列表
                    *  eventTypesFound需要存储事件处理方法和Class类名,在子类、父类、父类的父类中,事件处理方法名只有把一个(覆写)
                    *  Class名多个,规定只存储最继承层次最底层那个子类的Class作为value*/
                    /*如果子类没有覆写父类的事件处理方法,那么就把父类的事件处理方法加入事件处理方法列表*/
                    if (methodClassOld == null || methodClassOld.isAssignableFrom(methodClass)) {// Only add if not already found in a sub class
                        /*子类中没找到才添加*/
                       /* List<SubscriberMethod> subscriberMethods 方法传进来的参数*/
                        subscriberMethods.add(new SubscriberMethod(method, threadMode, eventType));} else {/*HashMap<String, Class> eventTypesFound 是传入的参数*/
                        /*eventTypesFound 中key为onEvent>AutoRefreshMsg这种格式的字符串,value为声明了该事件处理方法的类*/
                        // Revert the put, old class is further down the class hierarchy
                        /*注意是先处理子类后处理父类*/
                        eventTypesFound.put(methodKey, methodClassOld);}}} else if (!skipMethodVerificationForClasses.containsKey(methodClass)) {Log.d(EventBus.TAG, "Skipping method (not public, static or abstract): " + methodClass + "."
                        + methodName);}}}
}/**
 * 根据方法名称判断线程类型
 * @param clazz 订阅者
 * @param method
 * @param methodName 以onEvent开头的非static、abstract的public方法
 * @return
 */
private ThreadMode getThreadMode(Class<?> clazz, Method method, String methodName) {/*private static final String ON_EVENT_METHOD_NAME = "onEvent";*/
    /*去掉方法名前边的不变量onEvent,留下后边的变量区分线程类型*/
    String modifierString = methodName.substring(ON_EVENT_METHOD_NAME.length());ThreadMode threadMode;/*四种线程模型*/
    if (modifierString.length() == 0) {/*onEvent:回调函数和发起事件的函数会在同一个线程中执行*/
        threadMode = ThreadMode.PostThread;} else if (modifierString.equals("MainThread")) {/*onEventMainThread:回调函数会在主线程中执行,这个在Android中非常有用,因为在Android中禁止在子线程中修改UI*/
        threadMode = ThreadMode.MainThread;} else if (modifierString.equals("BackgroundThread")) {/*onEventBackgroundThread:如果在主线程发布事件,那么在子线程中处理该事件;
        吐过在子线程发布事件,那么事件处理方法就在这个子线程执行。*/
        threadMode = ThreadMode.BackgroundThread;} else if (modifierString.equals("Async")) {/*onEventBusAsync:不管在哪个线程发布事件,都会另起一个线程去处理事件。*/
        threadMode = ThreadMode.Async;} else {/*private final Map<Class<?>, Class<?>> skipMethodVerificationForClasses;*/
        /*判断是不是可以跳过订阅者方法校验,不跳过就派出异常,跳过就返回null*/
        if (!skipMethodVerificationForClasses.containsKey(clazz)) {throw new EventBusException("Illegal onEvent method, check for typos: " + method);} else {threadMode = null;}}return threadMode;
}

这篇关于EventBus源码解读详细注释(1)register的幕后黑手的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!



http://www.chinasem.cn/article/452055

相关文章

Redis与缓存解读

《Redis与缓存解读》文章介绍了Redis作为缓存层的优势和缺点,并分析了六种缓存更新策略,包括超时剔除、先删缓存再更新数据库、旁路缓存、先更新数据库再删缓存、先更新数据库再更新缓存、读写穿透和异步... 目录缓存缓存优缺点缓存更新策略超时剔除先删缓存再更新数据库旁路缓存(先更新数据库,再删缓存)先更新数

最新版IDEA配置 Tomcat的详细过程

《最新版IDEA配置Tomcat的详细过程》本文介绍如何在IDEA中配置Tomcat服务器,并创建Web项目,首先检查Tomcat是否安装完成,然后在IDEA中创建Web项目并添加Web结构,接着,... 目录配置tomcat第一步,先给项目添加Web结构查看端口号配置tomcat    先检查自己的to

使用Nginx来共享文件的详细教程

《使用Nginx来共享文件的详细教程》有时我们想共享电脑上的某些文件,一个比较方便的做法是,开一个HTTP服务,指向文件所在的目录,这次我们用nginx来实现这个需求,本文将通过代码示例一步步教你使用... 在本教程中,我们将向您展示如何使用开源 Web 服务器 Nginx 设置文件共享服务器步骤 0 —

Java汇编源码如何查看环境搭建

《Java汇编源码如何查看环境搭建》:本文主要介绍如何在IntelliJIDEA开发环境中搭建字节码和汇编环境,以便更好地进行代码调优和JVM学习,首先,介绍了如何配置IntelliJIDEA以方... 目录一、简介二、在IDEA开发环境中搭建汇编环境2.1 在IDEA中搭建字节码查看环境2.1.1 搭建步

SpringBoot集成SOL链的详细过程

《SpringBoot集成SOL链的详细过程》Solanaj是一个用于与Solana区块链交互的Java库,它为Java开发者提供了一套功能丰富的API,使得在Java环境中可以轻松构建与Solana... 目录一、什么是solanaj?二、Pom依赖三、主要类3.1 RpcClient3.2 Public

手把手教你idea中创建一个javaweb(webapp)项目详细图文教程

《手把手教你idea中创建一个javaweb(webapp)项目详细图文教程》:本文主要介绍如何使用IntelliJIDEA创建一个Maven项目,并配置Tomcat服务器进行运行,过程包括创建... 1.启动idea2.创建项目模板点击项目-新建项目-选择maven,显示如下页面输入项目名称,选择

Python基于火山引擎豆包大模型搭建QQ机器人详细教程(2024年最新)

《Python基于火山引擎豆包大模型搭建QQ机器人详细教程(2024年最新)》:本文主要介绍Python基于火山引擎豆包大模型搭建QQ机器人详细的相关资料,包括开通模型、配置APIKEY鉴权和SD... 目录豆包大模型概述开通模型付费安装 SDK 环境配置 API KEY 鉴权Ark 模型接口Prompt

在 VSCode 中配置 C++ 开发环境的详细教程

《在VSCode中配置C++开发环境的详细教程》本文详细介绍了如何在VisualStudioCode(VSCode)中配置C++开发环境,包括安装必要的工具、配置编译器、设置调试环境等步骤,通... 目录如何在 VSCode 中配置 C++ 开发环境:详细教程1. 什么是 VSCode?2. 安装 VSCo

Spring Boot 中整合 MyBatis-Plus详细步骤(最新推荐)

《SpringBoot中整合MyBatis-Plus详细步骤(最新推荐)》本文详细介绍了如何在SpringBoot项目中整合MyBatis-Plus,包括整合步骤、基本CRUD操作、分页查询、批... 目录一、整合步骤1. 创建 Spring Boot 项目2. 配置项目依赖3. 配置数据源4. 创建实体类

python与QT联合的详细步骤记录

《python与QT联合的详细步骤记录》:本文主要介绍python与QT联合的详细步骤,文章还展示了如何在Python中调用QT的.ui文件来实现GUI界面,并介绍了多窗口的应用,文中通过代码介绍... 目录一、文章简介二、安装pyqt5三、GUI页面设计四、python的使用python文件创建pytho