EventBus中SubscriberInfoIndex的使用

版权声明:本文为博主原创文章,未经博主允许不得转载。 https://blog.csdn.net/qq_18242391/article/details/80969638

前言

EventBus使用起来还是很方便的,它的源码是基于反射实现的,在3.0之后增加了注解处理器,在程序的编译时候,就可以根据注解生成相对应的代码,相对于之前的直接通过运行时反射,大大提高了程序的运行效率,但是在3.0默认的还是通过反射去查找用@Subscribe标注的方法,一般在使用的时候基本都是这个模式

//注册
if (!EventBus.getDefault().isRegistered(this)) {
   EventBus.getDefault().register(this)
}
//反注册
if (EventBus.getDefault().isRegistered(this)) {
   EventBus.getDefault().unregister(this)
}

虽然用起来还是很方便的,但是这种默认就是通过反射去查找在代码中注册的方法,这就在性能上有一定的损耗。所以通过这些时读EventBus3.0的源码的一点感悟,来谈谈注解处理器中SubscriberInfoIndexEventBus3.0的使用


SubscriberInfoIndex

EventBus的注册和发送事件的源码执行过程就不再这里讲了,这篇文章就讲得蛮清楚的Android开源框架源码鉴赏:EventBus

一般获取它的实例就是通过EventBus.getDefault()获取,看源码很明显就是一个静态单例的实现,但是我要说的是它的构造方法就是public的,说明外界就可以直接通过new EventBus获取其实例,而它的构造方法有一个

EventBus(EventBusBuilder builder) {
    ....
}

这样的重载,这里我们就重点关注下 EventBusBuilder,看到它其实就可以联想到EventBus类可以通过Builder构建者模式获取其实例

这里写图片描述

从这里就可以证明。

List<SubscriberInfoIndex> subscriberInfoIndexes;

EventBusBuilder中有这样一个变量,通过这个变量可以去使用注解处理器生成的代码。SubscriberInfoIndex 就是一个接口,而注解生成器生成的类也是继承的它,我们也可以自己去继承它,定制自己的需求,不需要反射的EventBus。

EventBus的源码中有这样一段代码

//SubscriberMethodFinder 
List<SubscriberMethod> findSubscriberMethods(Class<?> subscriberClass) {
      ....
        //ignoreGeneratedIndex默认为false,我们可以设置为true,忽略SubscriberInfoIndex的代码
        if (ignoreGeneratedIndex/*false*/) {
            subscriberMethods = findUsingReflection(subscriberClass);
        } else {
            subscriberMethods = findUsingInfo(subscriberClass); //走这一步
        }
      ... 
    }

private List<SubscriberMethod> findUsingInfo(Class<?> subscriberClass) {
        FindState findState = prepareFindState();
        findState.initForSubscriber(subscriberClass);
        while (findState.clazz != null) {
            findState.subscriberInfo = getSubscriberInfo(findState); //默认为null
            ...
        }
        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;
    }

如果subscriberInfoIndexes不为null,就会去调用SubscriberInfoIndex的getSubscriberInfo方法获取SubscriberInfo,SubscriberInfo这个变量就封装了注册方法的信息。看到这里,再来看官方提供给我们的例子

  • 1.) 官方Demo
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;
    }
}

Demo首先实例化了一个SubscriberInfoIndex匿名内部类,并且实现了getSubscriberInfo方法。在这个方法中创建了一个SubscriberMethodInfo数组,SubscriberMethodInfo封装了方法名,当前线程,参数的事件类型,优先级,是否是粘性事件,然后将SubscriberMethodInfo数组封装到SimpleSubscriberInfo中,参数名分别为当前类名的class, 是否应该检查父类,SubscriberMethodInfo数组,SimpleSubscriberInfoSubscriberInfo的实现类,最后将SimpleSubscriberInfo的实例化结果作为getSubscriberInfo的返回值。

到这里就构成了一个index,然后就去通过构建者模式获取EventBus的实例,剩下用法就和之前的一样的

1.index在EventBus内部其实就是添加到了subscriberInfoIndexes集合上,前面我们也看到在EventBus内部会判断subscriberInfoIndexes是否为null, 不为null直接获取里面封装的方法信息。这样就可以避免反射的影响。
2.我们也可以不必使用@Subscribe注解了

  • 2.) 注解处理器中的SubscriberInfoIndex

EventBus3.0增加了注解处理器,关于注解处理器我看了这篇文章比较详细。看完之后再来看EventBus的注解处理器这个模块应该有帮助。有个地方需要提下的就是下图

这里写图片描述

我们需要在build文件中配置OPTION_EVENT_BUS_INDEX,不然后面的不会执行,至于怎么配,看官方的一个demo

这里写图片描述

javaCompileOptions {
     annotationProcessorOptions {
         arguments = [ eventBusIndex : 'org.greenrobot.eventbus.EventBusTestsIndex' ]
     }
 }

模板代码直接复制就可以了。看下生成的类文件

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

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

        putIndex(new SimpleSubscriberInfo(EventBusAndroidOrderTest.class, true, new SubscriberMethodInfo[] {
            new SubscriberMethodInfo("onEvent", String.class, ThreadMode.MAIN),
            new SubscriberMethodInfo("onEvent", EventBusAndroidOrderTest.OrderedEvent.class, ThreadMode.MAIN_ORDERED),
        }));

    ....

    }

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

基本就是这种模式,然后我们就可以通过EventBus.builder().addIndex(index).build()这种形式去获取EventBus的实例,这样就可以避免大量反射在EventBus中使用造成的性能损耗。


总结

到了这里基本就完了,可以看到注解处理器还是很有用的,无论在EventBus,还是ButterKnife中都有广泛的使用,所以注解处理器这方面还是要多学习下的。另外,我们不能只是简单停留在Api级别的使用上,对于一些开源框架更深入的基本的原理还是需要掌握的,这样可以更好的使用它们,而不总是写些模板代码,学会了这些框架的设计思想,将它们运用到我们的项目中还是很有帮助的。

猜你喜欢

转载自blog.csdn.net/qq_18242391/article/details/80969638