EventBus 开源库学习(三)

源码细节阅读

上一节根据EventBus的使用流程把实现源码大体梳理了一遍,因为精力有限,所以看源码都是根据实现过程把基本流程看下,中间实现细节先忽略,否则越看越深不容易把握大体思路,这节把一些细节的部分再看看。

注解函数查找源码逻辑

#EventBus   public void register(Object subscriber) {Class<?> subscriberClass = subscriber.getClass();List<SubscriberMethod> subscriberMethods = subscriberMethodFinder.findSubscriberMethods(subscriberClass);synchronized (this) {for (SubscriberMethod subscriberMethod : subscriberMethods) {subscribe(subscriber, subscriberMethod);}}}

在进行注册的时候,我们使用subscriberMethodFinder对象的findSubscriberMethods方法来查找到所有该类中以@Subscribe注解的函数(以下简称为注解函数)。让我们继续看下查找部分的逻辑。

    List<SubscriberMethod> findSubscriberMethods(Class<?> subscriberClass) {List<SubscriberMethod> subscriberMethods = METHOD_CACHE.get(subscriberClass);if (subscriberMethods != null) {return subscriberMethods;}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;}}

METHOD_CACHE是一个ConcurrentHashMap类型的常量,用来保存subscriberClass和对应SubscriberMethod的集合,以提高注册效率,防止重复查找。

如果METHOD_CACHE缓存中不存在,判断变量ignoreGeneratedIndex的值,该值是在EventBusBuilder中初始化的,表示是否忽略注解生成器,默认是false。然后会进入到findUsingInfo函数中进行查找(下面再看),最后将找到的subscriberMethods添加到METHOD_CACHE中。

    private List<SubscriberMethod> findUsingInfo(Class<?> subscriberClass) {FindState findState = prepareFindState();findState.initForSubscriber(subscriberClass);````return getMethodsAndRelease(findState);}

先看findUsingInfo前两行,出现一个FindState类,它是SubscriberMethodFinder的内部类,用来辅助查找订阅事件的方法。

    private FindState prepareFindState() {synchronized (FIND_STATE_POOL) {for (int i = 0; i < POOL_SIZE; i++) {FindState state = FIND_STATE_POOL[i];if (state != null) {FIND_STATE_POOL[i] = null;return state;}}}return new FindState();}static class FindState {```void initForSubscriber(Class<?> subscriberClass) {this.subscriberClass = clazz = subscriberClass;skipSuperClasses = false;subscriberInfo = null;}}

prepareFindState()方法主要是返回一个FindState对象,先从FIND_STATE_POOL这个缓存池中获取一个现成的,如果没有就新建一个对象。获取对象后调用初始化方法,将subscriberClass赋值给clazz变量,subscriberInfo对象赋值为null

    private List<SubscriberMethod> findUsingInfo(Class<?> subscriberClass) {FindState findState = prepareFindState();findState.initForSubscriber(subscriberClass);while (findState.clazz != null) {findState.subscriberInfo = getSubscriberInfo(findState);if (findState.subscriberInfo != null) {```} else {findUsingReflectionInSingleClass(findState);}findState.moveToSuperclass();}return getMethodsAndRelease(findState);}

再回到findUsingInfo,因为findState.clazz刚赋值过,所以不为空。再来看下getSubscriberInfo方法。

    private SubscriberInfo getSubscriberInfo(FindState findState) {if (findState.subscriberInfo != null && findState.subscriberInfo.getSuperSubscriberInfo() != null) {SubscriberInfo superclassInfo = findState.subscriberInfo.getSuperSubscriberInfo();if (findState.clazz == superclassInfo.getSubscriberClass()) {return superclassInfo;}}if (subscriberInfoIndexes != null) {for (SubscriberInfoIndex index : subscriberInfoIndexes) {SubscriberInfo info = index.getSubscriberInfo(findState.clazz);if (info != null) {return info;}}}return null;}

前面执行initForSubscriber的时候,subscriberInfo赋值为null,因此第一个if不成立。第二个判断是对象subscriberInfoIndexes,该变量也是在EventBusBuilder中初始化的,默认是null,因此该方法返回值null

    private List<SubscriberMethod> findUsingInfo(Class<?> subscriberClass) {FindState findState = prepareFindState();findState.initForSubscriber(subscriberClass);while (findState.clazz != null) {findState.subscriberInfo = getSubscriberInfo(findState);if (findState.subscriberInfo != null) {```} else {findUsingReflectionInSingleClass(findState);}findState.moveToSuperclass();}return getMethodsAndRelease(findState);}

再回到findUsingInfo,由于getSubscriberInfo返回为null,逻辑会走到findUsingReflectionInSingleClass方法中,使用反射来获取所有方法(下面再看)。

获取当前类所有的注解方法后,findState.moveToSuperclass()表示修改findState.clazzsubscriberClass的父类Class,继续遍历父类中的注解方法。

private static final int MODIFIERS_IGNORE = Modifier.ABSTRACT | Modifier.STATIC | BRIDGE | SYNTHETIC;private void findUsingReflectionInSingleClass(FindState findState) {Method[] methods;try {// This is faster than getMethods, especially when subscribers are fat classes like Activitiesmethods = findState.clazz.getDeclaredMethods();} catch (Throwable th) {// Workaround for java.lang.NoClassDefFoundError, see https://github.com/greenrobot/EventBus/issues/149```}for (Method method : methods) {int modifiers = method.getModifiers();if ((modifiers & Modifier.PUBLIC) != 0 && (modifiers & MODIFIERS_IGNORE) == 0) {Class<?>[] parameterTypes = method.getParameterTypes();if (parameterTypes.length == 1) {Subscribe subscribeAnnotation = method.getAnnotation(Subscribe.class);if (subscribeAnnotation != null) {Class<?> eventType = parameterTypes[0];if (findState.checkAdd(method, eventType)) {ThreadMode threadMode = subscribeAnnotation.threadMode();findState.subscriberMethods.add(new SubscriberMethod(method, eventType, threadMode,subscribeAnnotation.priority(), subscribeAnnotation.sticky()));}}} else if (strictMethodVerification && method.isAnnotationPresent(Subscribe.class)) {String methodName = method.getDeclaringClass().getName() + "." + method.getName();throw new EventBusException("@Subscribe method " + methodName +"must have exactly 1 parameter but has " + parameterTypes.length);}} else if (strictMethodVerification && method.isAnnotationPresent(Subscribe.class)) {String methodName = method.getDeclaringClass().getName() + "." + method.getName();throw new EventBusException(methodName +" is a illegal @Subscribe method: must be public, non-static, and non-abstract");}}}

上面代码流程如下:

  • 通过方法getDeclaredMethods()获取该类中所有声明的方法列表;
  • 遍历方法列表,获取方法的修饰符,如果修饰符是public并且不是abstractstatic等进入下一步,MODIFIERS_IGNORE定义如第一行代码;
  • 获取方法的参数列表,如果参数的个数是1,并且使用了Subscribe注解,获取唯一的入参进行下一步;
  • 然后checkAdd()方法用来判断FindState的anyMethodByEventType map是否已经添加过以当前eventTypekey的键值对,没添加过则返回true(就是看看当前类中有没有多个事件相同的注解函数);
  • 通过注解对象subscribeAnnotation获取threadMode,然后新建SubscriberMethod添加到findState.subscriberMethods集合中。

分析完findUsingReflectionInSingleClass这个方法后,我们回到findUsingInfo

    private List<SubscriberMethod> findUsingInfo(Class<?> subscriberClass) {FindState findState = prepareFindState();findState.initForSubscriber(subscriberClass);while (findState.clazz != null) {findState.subscriberInfo = getSubscriberInfo(findState);if (findState.subscriberInfo != null) {```} else {findUsingReflectionInSingleClass(findState);}findState.moveToSuperclass();}return getMethodsAndRelease(findState);}

遍历当前类及其父类,通过findUsingReflectionInSingleClass方法找到所有注解方法后,通过getMethodsAndRelease返回所有相关方法。

    private List<SubscriberMethod> getMethodsAndRelease(FindState findState) {List<SubscriberMethod> subscriberMethods = new ArrayList<>(findState.subscriberMethods);findState.recycle();synchronized (FIND_STATE_POOL) {for (int i = 0; i < POOL_SIZE; i++) {if (FIND_STATE_POOL[i] == null) {FIND_STATE_POOL[i] = findState;break;}}}return subscriberMethods;}

通过上面的解析知道,所有的SubscriberMethod都添加到findState.subscriberMethods集合中,这个方法就是把findState.subscriberMethods集合中的内容复制出来,然后释放findState中的资源,并把findState放到FIND_STATE_POOL缓存中,POOL_SIZE常量是4,这样下次查找的时候可以重复利用无需每次都新建对象。

以上为注解方法查找的过程。在上面分析的过程中出现了两个变量subscriberInfoIndexesignoreGeneratedIndex看样子都是关于索引的,分析的时候都是使用的EventBusBuilder中的默认值。看下subscriberInfoIndexes赋值的地方,看注释意思是:将EventBus注解处理器生成索引添加添加进来。所以注解处理器需要再了解下。

    /** Adds an index generated by EventBus' annotation preprocessor. */public EventBusBuilder addIndex(SubscriberInfoIndex index) {if (subscriberInfoIndexes == null) {subscriberInfoIndexes = new ArrayList<>();}subscriberInfoIndexes.add(index);return this;}

Subscriber Index注解处理器

通过上面分析可知,EventBus注册事件时,主要是在项目运行时通过反射来查找注解方法信息,如果项目中有大量的注解方法,必然会对项目运行时的性能产生影响。

除了在项目运行时通过反射查找注解方法信息,EventBus 还提供了在项目编译时通过注解处理器查找注解方法信息的方式,生成一个辅助的索引类来保存这些信息,这个索引类就是Subscriber Index。

1、Subscriber Index使用方式
首先要在 appbuild.gradle中加入如下配置:

android {defaultConfig {javaCompileOptions {annotationProcessorOptions {// 指定辅助索引类的名称和包名,注意,这个类时自动生成的,不需要我们写arguments = [ eventBusIndex : 'com.jane.demo.MyEventBusIndex' ]}}}
}
dependencies {compile 'org.greenrobot:eventbus:3.3.1'// 引入注解处理器annotationProcessor 'org.greenrobot:eventbus-annotation-processor:3.3.1'
}

在项目的Application中添加如下配置,以生成一个默认的 EventBus 单例:

EventBus.builder().addIndex(new MyEventBusIndex()).installDefaultEventBus();

此时重新编译下项目,会生成一个MyEventBusIndex类(不同的gradle版本生成的位置可能不一样)。
MyEventBusIndex.png

生成的MyEventBusIndex代码如下:

public class MyEventBusIndex implements SubscriberInfoIndex {private static final Map<Class<?>, SubscriberInfo> SUBSCRIBER_INDEX;static {SUBSCRIBER_INDEX = new HashMap<Class<?>, SubscriberInfo>();putIndex(new SimpleSubscriberInfo(EventBusService.class, true, new SubscriberMethodInfo[] {new SubscriberMethodInfo("onMsgEventReceived", String.class),new SubscriberMethodInfo("onMsgEventReceived", MsgEvent.class),new SubscriberMethodInfo("onMsgEventReceived", org.greenrobot.eventbus.NoSubscriberEvent.class),}));putIndex(new SimpleSubscriberInfo(EventBusActivity.class, true, new SubscriberMethodInfo[] {new SubscriberMethodInfo("onMsgEventReceived", Event.class),}));}private static void putIndex(SubscriberInfo info) {SUBSCRIBER_INDEX.put(info.getSubscriberClass(), info);}@Overridepublic SubscriberInfo getSubscriberInfo(Class<?> subscriberClass) {SubscriberInfo info = SUBSCRIBER_INDEX.get(subscriberClass);if (info != null) {return info;} else {return null;}}
}

代码中SUBSCRIBER_INDEX是一个HashMap,保存了当前注册类的Class类型和其中注解方法的信息。

2、Subscriber Index源码

EventBus.builder().addIndex(new MyEventBusIndex()).installDefaultEventBus();

使用的时候,获取EventBusBuilder对象,然后调用addIndex方法,把生成的MyEventBusIndex加入进去。

    /** Adds an index generated by EventBus' annotation preprocessor. */public EventBusBuilder addIndex(SubscriberInfoIndex index) {if (subscriberInfoIndexes == null) {subscriberInfoIndexes = new ArrayList<>();}subscriberInfoIndexes.add(index);return this;}

上面的方法是不是比较眼熟,就是上面代码分析的时候出现的变量subscriberInfoIndexes赋值函数。这样,运行的时候就把之前编译好的索引类的实例保存在subscriberInfoIndexes集合中。然后调用installDefaultEventBus方法创建EventBus实例。

    /*** Installs the default EventBus returned by {@link EventBus#getDefault()} using this builders' values. Must be* done only once before the first usage of the default EventBus.** @throws EventBusException if there's already a default EventBus instance in place*/public EventBus installDefaultEventBus() {synchronized (EventBus.class) {if (EventBus.defaultInstance != null) {throw new EventBusException("Default instance already exists." +" It may be only set once before it's used the first time to ensure consistent behavior.");}EventBus.defaultInstance = build();return EventBus.defaultInstance;}}/** Builds an EventBus based on the current configuration. */public EventBus build() {return new EventBus(this);}

上面通过build方法,以当前EventBusBuilder对象作为参数生成EventBus单例对象,并赋值给defaultInstance,这样就把subscriberInfoIndexes信息传递给了EventBus。以后调用EventBus getDefault()返回的就是这里生成的单例对象,里面包含了subscriberInfoIndexes信息。

而且在Application中生成了 EventBus 的默认单例,这样就保证了在项目其它地方执行EventBus.getDefault()就能得到就是上面包含subscriberInfoIndexes信息的单例。

在上面分析查找注解函数的时候,分析过一个函数:

    private List<SubscriberMethod> findUsingInfo(Class<?> subscriberClass) {FindState findState = prepareFindState();findState.initForSubscriber(subscriberClass);while (findState.clazz != null) {findState.subscriberInfo = getSubscriberInfo(findState);if (findState.subscriberInfo != null) {····} else {findUsingReflectionInSingleClass(findState);}findState.moveToSuperclass();}return getMethodsAndRelease(findState);}

如果没有使用注解处理器的话getSubscriberInfo这个方法返回值是null,因此会走到findUsingReflectionInSingleClass,通过反射区获取注解方法。现在使用使用注解处理器的话在重新看下这个函数。

    private SubscriberInfo getSubscriberInfo(FindState findState) {if (findState.subscriberInfo != null && findState.subscriberInfo.getSuperSubscriberInfo() != null) {SubscriberInfo superclassInfo = findState.subscriberInfo.getSuperSubscriberInfo();if (findState.clazz == superclassInfo.getSubscriberClass()) {return superclassInfo;}}//不为空if (subscriberInfoIndexes != null) {for (SubscriberInfoIndex index : subscriberInfoIndexes) {SubscriberInfo info = index.getSubscriberInfo(findState.clazz);if (info != null) {return info;}}}return null;}@Overridepublic SubscriberInfo getSubscriberInfo(Class<?> subscriberClass) {SubscriberInfo info = SUBSCRIBER_INDEX.get(subscriberClass);if (info != null) {return info;} else {return null;}}

如果使用了注解处理器,因为subscriberInfoIndexes这个集合不为空,遍历获取当前类对应的注解方法列表, index.getSubscriberInfo这个方法实际调用的就是系统生成的MyEventBusIndex中的getSubscriberInfo方法,这样就获取到了之前编译生成的注解方法。

    private List<SubscriberMethod> findUsingInfo(Class<?> subscriberClass) {FindState findState = prepareFindState();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);}

通过getSubscriberInfo返回当前类所有注解方法后,遍历所有订阅了事件的方法然后加入到subscriberMethods列表中,其它的和之前的注册流程一样。

参考文章:
EventBus 原理解析

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.hqwc.cn/news/55289.html

如若内容造成侵权/违法违规/事实不符,请联系编程知识网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

OA办公自动化系统设计与实现(论文+源码)_kaic

摘要 随着信息化建设的日益深入&#xff0c;无论是政府还是企事业单位&#xff0c;部门之间的信息沟通与协调工作越来越重要。人们迫切需要一个能充分利用网络优势&#xff0c;并可以管理企业的各种重要信息的软件平台&#xff0c;利用该平台快速建立自己的信息网络和办公管理系…

C语言案例 按序输出多个整数-03

难度2复杂度3 题目&#xff1a;输入多个整数&#xff0c;按从小到大的顺序输出 步骤一&#xff1a;定义程序的目标 编写一个C程序&#xff0c;随机输入整数&#xff0c;按照从小到大的顺序输出 步骤二&#xff1a;程序设计 整个C程序由三大模块组成&#xff0c;第一个模块使…

利用XSS在线平台获取用户cookie

//XSS弹窗&#xff1a; <script>alert("xss")</script> XSS漏洞&#xff1a; //XSS弹窗&#xff1a; <script>alert("xss")</script> //XSS在线平台&#xff1a; <ScRipT sRc//7ix7kigpovxdbtd32fuspgffmtmufo3wwzgnzaltddewtb…

应急响应-勒索病毒的处理思路

0x00 关于勒索病毒的描述 勒索病毒入侵方式&#xff1a;服务弱口令&#xff0c;未授权&#xff0c;邮件钓鱼&#xff0c;程序木马植入&#xff0c;系统漏洞等 勒索病毒的危害&#xff1a;主机文件被加密&#xff0c;且几乎难以解密&#xff0c;对主机上的文件信息以及重要资产…

C# PaddleDetection 版面分析

效果 项目 代码 using OpenCvSharp; using OpenCvSharp.Extensions; using Sdcb.PaddleDetection; using Sdcb.PaddleInference; using System; using System.Collections.Generic; using System.ComponentModel; using System.Data; using System.Drawing; using System.Lin…

2023-08-06 LeetCode每日一题(24. 两两交换链表中的节点)

2023-08-06每日一题 一、题目编号 24. 两两交换链表中的节点二、题目链接 点击跳转到题目位置 三、题目描述 给你一个链表&#xff0c;两两交换其中相邻的节点&#xff0c;并返回交换后链表的头节点。你必须在不修改节点内部的值的情况下完成本题&#xff08;即&#xff0…

ffmpeg-ffplay代码架构简述

全局变量 /* Minimum SDL audio buffer size, in samples. */ // 最小音频缓冲 #define SDL_AUDIO_MIN_BUFFER_SIZE 512 /* Calculate actual buffer size keeping in mind not cause too frequent audio callbacks */ // 计算实际音频缓冲大小&#xff0c;并不需要太频繁…

2023年8月初补题

看这个人的专栏 https://blog.csdn.net/qq_42555009/category_8770183.html 有一定思维难度&#xff0c;贪心&#xff0c;用multiset实现 翻译&#xff1a; 链接&#xff1a;https://ac.nowcoder.com/acm/contest/33187/H 来源&#xff1a;牛客网 在夜之城的中心有一座高大…

剑指offer65.不用加减乘除做加法

把二进制加法可以分为五进位加法和进位加法&#xff0c;无进位&#xff1a;000&#xff0c;011&#xff0c;101&#xff0c;有进位加法&#xff1a;110进位为1。可以发现无进位的加法与异或运算规律相同&#xff0c;有进位加法和与运算规律相同&#xff0c;无进位和na^b,有进位…

安装linux操作系统

安装虚拟机的步骤&#xff1a; 安装linux系统 之后开启虚拟机 之后重启&#xff0c;打开虚拟机&#xff0c;登录root账号

基于Pyqt5+serial的串口电池监测工具

本章&#xff0c;其他的没有&#xff0c;废话没有&#xff0c;介绍一下新开源了一个公司的测试工具&#xff0c;写了差不多三周吧。先来看看界面&#xff1a; 这是一个串口调试界面&#xff0c;使用Pyqt5serial完成。升级功能暂未移入&#xff0c;占一个坑位。 基于serial二次开…

接口测试之文件上传

在日常工作中&#xff0c;经常有上传文件功能的测试场景&#xff0c;因此&#xff0c;本文介绍两种主流编写上传文件接口测试脚本的方法。 首先&#xff0c;要知道文件上传的一般原理&#xff1a;客户端根据文件路径读取文件内容&#xff0c;将文件内容转换成二进制文件流的格式…