EventBus 开源库学习(三)

源码细节阅读

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

注解函数查找源码逻辑

java 复制代码
#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注解的函数(以下简称为注解函数)。让我们继续看下查找部分的逻辑。

java 复制代码
    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中。

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

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

java 复制代码
    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

java 复制代码
    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方法。

java 复制代码
    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

java 复制代码
    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,继续遍历父类中的注解方法。

java 复制代码
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 Activities
            methods = 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

java 复制代码
    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返回所有相关方法。

java 复制代码
    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注解处理器生成索引添加添加进来。所以注解处理器需要再了解下。

java 复制代码
    /** 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中加入如下配置:

java 复制代码
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代码如下:

java 复制代码
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);
    }

    @Override
    public 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源码

java 复制代码
EventBus.builder().addIndex(new MyEventBusIndex()).installDefaultEventBus();

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

java 复制代码
    /** 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实例。

java 复制代码
    /**
     * 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信息的单例。

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

java 复制代码
    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,通过反射区获取注解方法。现在使用使用注解处理器的话在重新看下这个函数。

java 复制代码
    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;
    }

    @Override
    public SubscriberInfo getSubscriberInfo(Class<?> subscriberClass) {
        SubscriberInfo info = SUBSCRIBER_INDEX.get(subscriberClass);
        if (info != null) {
            return info;
        } else {
            return null;
        }
    }

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

java 复制代码
    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 原理解析

相关推荐
DKPT1 小时前
Java桥接模式实现方式与测试方法
java·笔记·学习·设计模式·桥接模式
程序视点3 小时前
Window 10文件拷贝总是卡很久?快来试试这款小工具,榨干硬盘速度!
windows
AI生存日记3 小时前
百度文心大模型 4.5 系列全面开源 英特尔同步支持端侧部署
人工智能·百度·开源·open ai大模型
wuk9983 小时前
基于MATLAB编制的锂离子电池伪二维模型
linux·windows·github
好好研究4 小时前
学习栈和队列的插入和删除操作
数据结构·学习
步、步、为营4 小时前
.net开源库SignalR
开源·.net
lzb_kkk4 小时前
【C++】C++四种类型转换操作符详解
开发语言·c++·windows·1024程序员节
新中地GIS开发老师4 小时前
新发布:26考研院校和专业大纲
学习·考研·arcgis·大学生·遥感·gis开发·地理信息科学
SH11HF5 小时前
小菜狗的云计算之旅,学习了解rsync+sersync实现数据实时同步(详细操作步骤)
学习·云计算
Frank学习路上5 小时前
【IOS】XCode创建firstapp并运行(成为IOS开发者)
开发语言·学习·ios·cocoa·xcode