event-bus/

EventBus虽然用起来很方便,但是通过前面讲的我们发现在register过程中用到了大量的反射。这就导致了天然的性能缺陷。

引言

前面讲到了EventBus在findUsingInfo的时候会根据FindState获取SubscriberInfo,这个过程是通过调用getSubscriberInfo(findState);来获取到的。

现在复习一下这段代码:

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

可以看到如果字节拿到SubscriberInfo就可以不经过findUsingReflectionInSingleClass来反射了。

下面来看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;
}

主要看subscriberInfoIndexes,如果可以通过SubscriberInfoIndex获取到对应的SubscriberInfo,就可以不反射了。

使用Index

首先记得在build.gradle文件中添加processer依赖。如下:

apply plugin: 'com.neenbedankt.android-apt'

apt {
    arguments {
        eventBusIndex "me.yourbay.test.EventAnnoIndex"
    }
}

apt 'org.greenrobot:eventbus-annotation-processor:3.0.1'

此时可以在EventBus中注册arguments里面的eventBusIndex。如下:

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

这里使用的是默认的Builder,所以这里会添加到默认的EventBus对象中。如果你自己写了config,放到自己的builder中即可。

注意,此时其实ide会报找不到EventAnnoIndex错误。那是因为你还没有编译,先编译下然后就不会有错误了。

至此,有没有发现都叫index,其实是跟前面说的index对上了。这里addIndex其实就是放到subscriberInfoIndexes这个列表中。

下面我们看看EventAnnoIndex到底干了什么。

eventBusIndex

其实上面配置的index只是编译之后的Index文件目录。

event-bus-index-file

打开看看是啥内容:

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

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

        putIndex(new SimpleSubscriberInfo(me.yourbay.test.ui.frags._3rd.EventBusFragment.class, true,
                new SubscriberMethodInfo[] {
            new SubscriberMethodInfo("onEvent", me.yourbay.test.ui.frags._3rd.EventBusFragment.Event.class),
            new SubscriberMethodInfo("onMainEvent",
                    me.yourbay.test.ui.frags._3rd.EventBusFragment.EventThreadMain.class, ThreadMode.MAIN),
            new SubscriberMethodInfo("onChildEvent",
                    me.yourbay.test.ui.frags._3rd.EventBusFragment.EventThreadChild.class, ThreadMode.BACKGROUND),
        }));

    }

    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, 可以看到getSubscriberInfo的时候回去这个map里面拿到对应的SubscriberInfo了。在SUBSCRIBER_INDEX初始化的时候会为每个Subscriber class生成一个SimpleSubscriberInfo实例,注意最后一个参数。是一个SubscriberMethodInfo[],对应着EventBusFragment里面的所有订阅函数。

下面我看看findState.subscriberInfo.getSubscriberMethods的时候干了什么。

@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 = 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);
    }
}

这里用到了上面的SubscriberMethodInfo[]函数。然后通过反射拿到对应的订阅函数。也就是说还是会通过反射。但是不用遍历class里面的每一个DeclaredMethod,以及去掉获取Annotation(Subscribe.class)ParameterTypes的开销,这样可以减少很多系统开销。但是之后的subscribe和post等也是通过原来的方式调用。

至于EventAnnoIndex是如何生成的,可以去看看https://github.com/greenrobot/EventBus/tree/master/EventBusAnnotationProcessor里面实现,简单来说就是Ide编译的时候会调用EventBusAnnotationProcessor里面的process方法,在这里将所有的文件过一遍,然后写入到index里面。类似于ButterKnifeAnnotationProcesser。

其实这种方式不是很彻底,只是减少了获取订阅函数的开销而已。并不会像ButterKnife那样有效的减少反射的使用次数。

By @hyongbai 共5514个字

本文链接 http://yourbay.me/all-about-tech/2017/03/23/event-bus-annotation/