EventBus中的SubscriberInfoIndex

本文解析EventBus 3.0版本中引入的索引优化机制,详细介绍了如何通过配置索引类提升应用运行效率,避免运行时反射带来的性能损耗。文章深入探讨了索引类的生成过程及其实现原理,展示了如何在编译期间捕获@Subscribe注解,生成订阅信息索引。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

EventBus源码解析中的findUsingInfo方法中有说到如果没有配置索引类,那么就会调用findUsingReflectionInSingleClass方法,在运行时利用反射来获取所有被@Subscribe注解标注的订阅方法,但这种做法对应用性能会有一定的损耗,所以在3.0后EventBus提供了索引来提高应用运行效率。

首先,在app下的build.gradle添加依赖和对应的配置:

apply plugin: 'kotlin-kapt'

dependencies {
    ...
    implementation 'org.greenrobot:eventbus:3.1.1'
    kapt 'org.greenrobot:eventbus-annotation-processor:3.1.1'
}

kapt {
    arguments {
    	// 指定索引类的全路径名
        arg('eventBusIndex', 'com.ljw.eventbus_index.MyEventBusIndex')
    }
}

在Activity编写个测试的订阅方法:

@Subscribe
  public fun doSomething(singleEvent: SingleEvent){
      println("singleEvent: ${singleEvent.value}")
}

在编译期间会通过注解处理器去查找使用@Subscribe注解标注的方法,生成一个实现了SubscriberInfoIndex接口的类:

/** This class is generated by EventBus, do not edit. */
public class MyEventBusIndex implements SubscriberInfoIndex {
    private static final Map<Class<?>, SubscriberInfo> SUBSCRIBER_INDEX;

    static {
        SUBSCRIBER_INDEX = new HashMap<Class<?>, SubscriberInfo>();

        // SubscriberMethodInfo方法包含了方法名、事件类型等信息
        // 在编译时期获取到所有的订阅信息,并且保存在Map中
        putIndex(new SimpleSubscriberInfo(MainActivity.class, true, new SubscriberMethodInfo[] {
            new SubscriberMethodInfo("doSomething", SingleEvent.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;
        }
    }
}

在该索引类中获取所有的订阅信息,保存到Map中,再看下SimpleSubscriberInfo内的处理:

public class SimpleSubscriberInfo extends AbstractSubscriberInfo {

    private final SubscriberMethodInfo[] methodInfos;

    public SimpleSubscriberInfo(Class subscriberClass, boolean shouldCheckSuperclass, SubscriberMethodInfo[] methodInfos) {
        super(subscriberClass, null, shouldCheckSuperclass);
        this.methodInfos = methodInfos;
    }

    @Override
    public synchronized SubscriberMethod[] getSubscriberMethods() {
        int length = methodInfos.length;
        SubscriberMethod[] methods = new SubscriberMethod[length];
        for (int i = 0; i < length; i++) {
            SubscriberMethodInfo info = methodInfos[i];

            methods[i] = createSubscriberMethod(info.methodName, info.eventType, info.threadMode,
                    info.priority, info.sticky);
        }
        return methods;
    }
}

protected SubscriberMethod createSubscriberMethod(String methodName, Class<?> eventType, ThreadMode threadMode,
                                                  int priority, boolean sticky) {
    try {
        // 通过方法名和事件类型获取到Method对象
        Method method = subscriberClass.getDeclaredMethod(methodName, eventType);
        // 构造订阅方法并返回
        return new SubscriberMethod(method, eventType, threadMode, priority, sticky);
    } catch (NoSuchMethodException e) {
        throw new EventBusException("Could not find subscriber method in " + subscriberClass +
                ". Maybe a missing ProGuard rule?", e);
    }
}

可以看到,生成的索引类主要是保存了订阅类与它所有的订阅信息的映射,外部通过getSubscriberInfo就能获取到对应订阅类的订阅信息。回到EventBus源码解析#findUsingInfo方法这里:

private List<SubscriberMethod> findUsingInfo(Class<?> subscriberClass) {
   ... 省略部分代码
    // 循环直到findState的clazz为空(clazz为java.或android.或javax.等系统类则结束查找)
    while (findState.clazz != null) {
        // 由于subscriberInfoIndexes()为空,则该方法就返回null
        // subscriberInfoIndexes(注解处理器生成的索引类),通过EventBus.builder().addIndex(索引类)添加进来
        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);
        }

        // 将clazz指向订阅类的父类,直到找到java.或android.或javax.等系统类,则返回null
        findState.moveToSuperclass();
    }
    // 获取订阅方法集合并释放FindState
    return getMethodsAndRelease(findState);
}

private SubscriberInfo getSubscriberInfo(FindState findState) {
    ... 省略代码
    if (subscriberInfoIndexes != null) {
        for (SubscriberInfoIndex index : subscriberInfoIndexes) {
            SubscriberInfo info = index.getSubscriberInfo(findState.clazz);
            if (info != null) {
                return info;
            }
        }
    }
    return null;
}

在有配置索引类的情况下,这里通过调用getSubscriberInfo就能获取到订阅者的所有的订阅方法信息。subscriberInfoIndexes是在构造方法传入的,所以使用索引的话需要使用EventBus.builder().addIndex(MyEventBusIndex()).installDefaultEventBus()来创建EventBus对象。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值