EventBus3.x源码分析之注册(二)

EventBus3.x源码分析之注册(二)

上篇文章分析了通用的订阅方法注册,其实EventBus3.0以后提供了一种更加高效的注册方式;
主要思路:

就是配合meta包中提供的SubscriberInfo接口,通过EventBusAnnotationProcessor编译时生成所需的订阅信息,用者只需要在EventBus的构建类EventBusBuilder中通过addIndex添加即可完成订阅。
配置依赖

官方文档:http://greenrobot.org/eventbus/documentation/subscriber-index/

How to generate the index
Gradle中配置如下代码:
    android {
        defaultConfig {
            javaCompileOptions {
                annotationProcessorOptions {
                    //这里可以指定编译时临时生成的SubscriberInfoIndex实例
                    //通过编译时生成的订阅信息是被放到EventBusDemo\app\build\generated\source\apt\debug\com\yiche\eventbusdemo\MyEventBusIndex.java中的
                    arguments = [ eventBusIndex : 'com.yiche.eventbusdemo.MyEventBusIndex' ]
                }
            }
        }
    }

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

自动生成订阅信息代码的类为org.greenrobot.eventbus.annotationprocessor.EventBusAnnotationProcessor,本人也只是了解部分原理,就不做分析了。
通过这个类会将方法中带有@Subscribe注解,public,非static等等一系列筛选后将订阅方法自动生成到MyEventBusIndex中:

/** This class is generated by EventBus, do not edit. */
//以下便是自动生成的SubscriberInfoIndex实例
public class MyEventBusIndex implements SubscriberInfoIndex {
    private static final Map<Class<?>, SubscriberInfo> SUBSCRIBER_INDEX;

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

        putIndex(new SimpleSubscriberInfo(MainActivity.class, true, new SubscriberMethodInfo[] {
            new SubscriberMethodInfo("onEventComming", String.class, ThreadMode.POSTING, 2, false),
            new SubscriberMethodInfo("onEventTest", String.class, ThreadMode.POSTING, 1, false),
        }));

    }

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

在编译时以一种固定格式生成该类,静态代码块中会最先将订阅类包括订阅方法封装好,通过getSubscriberInfo(Class

SubscriberMethodFinder#
private static final Map<Class<?>, List<SubscriberMethod>> METHOD_CACHE = new ConcurrentHashMap<>();
List<SubscriberMethod> findSubscriberMethods(Class<?> subscriberClass) {
    List<SubscriberMethod> subscriberMethods = METHOD_CACHE.get(subscriberClass);
    if (subscriberMethods != null) {
        return subscriberMethods;
    }
    //是否忽略注册时通过addIndex方法添加的订阅信息(属于性能优化)
    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 {
        //以订阅类名为key,订阅类中的订阅方法为value存入订阅方法的缓存容器(同步安全效率高的Hasmap,采用分段锁技术)中。
        METHOD_CACHE.put(subscriberClass, subscriberMethods);
        return subscriberMethods;
    }
}

其实ignoreGeneratedIndex默认是false就是希望通过这种方式也添加订阅方法,会走到

SubscriberMethodFinder#
private List<SubscriberMethod> findUsingInfo(Class<?> subscriberClass) {
   FindState findState = prepareFindState();
   findState.initForSubscriber(subscriberClass);
   //在当前类和自定义的父类中找不到订阅方法了为止
   while (findState.clazz != null) {
       findState.subscriberInfo = getSubscriberInfo(findState);
       if (findState.subscriberInfo != null) {
           //从通过addIndex方法,添加的订阅信息找到并添加
           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);
}

在获取findState.subscriberInfo = getSubscriberInfo(findState);的时候

private SubscriberInfo getSubscriberInfo(FindState findState) {
    //getSuperSubscriberInfo()会一直为null,但可以重写AbstractSubscriberInfo的实现类,在super中传入SubscriberInfo的实例即可
    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;
    }

这里找到编译生成订阅信息的关键方法!
会遍历subscriberInfoIndexes这个list,这个list就是在EventBusBuilder的addIndex方法添加的,添加以后就会执行这个if语句代码块,找到订阅信息并返回。
区别:
1、正常情况注册时在运行时要筛选方法(修饰符,参数个数,是否带有注解),然后进行方法去重check,运行时添加到订阅列表
2、而这里面的一系列订阅信息都是通过编译生成的,只需要进行是否为重复方法的check校验,因为这些都在编译时完成了,运行时只需要取出订阅即可,大大提升了性能。

当然也可以自己手动创建SubscriberInfo实例,官方也在测试代码中给出了例子。也可以根据分析这些代码,梳理出来自动生成的代码时如何将那些方法编译时生成订阅信息的。

测试代码块中的:EventBusIndexTest#testManualIndexWithoutAnnotation

public class EventBusIndexTest {
    private String value;

    /** Ensures the index is actually used and no reflection fall-back kicks in. */
    @Test
    public void testManualIndexWithoutAnnotation() {
        SubscriberInfoIndex index = new SubscriberInfoIndex() {

            @Override
            public SubscriberInfo getSubscriberInfo(Class<?> subscriberClass) {
                Assert.assertEquals(EventBusIndexTest.class, subscriberClass);
                SubscriberMethodInfo[] methodInfos = {
                        new SubscriberMethodInfo("someMethodWithoutAnnotation", String.class)
                };
                return new SimpleSubscriberInfo(EventBusIndexTest.class, false, methodInfos);
            }
        };

        EventBus eventBus = EventBus.builder().addIndex(index).build();
        eventBus.register(this);
        eventBus.post("Yepp");
        eventBus.unregister(this);
        Assert.assertEquals("Yepp", value);
    }

    public void someMethodWithoutAnnotation(String value) {
        this.value = value;
    }
}

EventBusBuilder将通过addIndex方法传入了的实现类。

在通过getSubscriberInfo(FindState findState)中,会在SubscribeInfo实现类中查找一系列的订阅方法信息。
实现类如下:

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

这里在index.getSubscriMethods()时,通过指定的方法名和方法类型快速的找到需要订阅的方法

public abstract class AbstractSubscriberInfo implements SubscriberInfo {
    private final Class subscriberClass;
    private final Class<? extends SubscriberInfo> superSubscriberInfoClass;
    private final boolean shouldCheckSuperclass;

    protected AbstractSubscriberInfo(Class subscriberClass, Class<? extends SubscriberInfo> superSubscriberInfoClass,
                                     boolean shouldCheckSuperclass) {
        this.subscriberClass = subscriberClass;
        this.superSubscriberInfoClass = superSubscriberInfoClass;
        this.shouldCheckSuperclass = shouldCheckSuperclass;
    }
    ...

    protected SubscriberMethod createSubscriberMethod(String methodName, Class<?> eventType, ThreadMode threadMode,
                                                      int priority, boolean sticky) {
        try {
            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);
        }
    }

}

将需要订阅的方法及参数类型先封装好,传入这个实例,通过getSubscriberMethods()在将这些方法找出来通过父类的createSubscriberMethod方法封装到SubscriberMethod中。
一般手动创建这种方式也不会被使用到。

总结:

addIndex(SubscriberInfo info)这种方式就是找到程序中带有@Subscribe注解的方法,生成subscribtioninfo实例时会检验你写的方法的合法性,一旦通过,它会对此方法信任并生成那个实例,在运行时需要订阅的时候直接通过方法名和参数类型进行获取。这就是提高性能的关键。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值