专栏首页终身开发者100行代码拆解EventBus核心逻辑(二)

100行代码拆解EventBus核心逻辑(二)

关于我 一个有思想的程序猿,终身学习实践者,目前在一个创业团队任team lead,技术栈涉及Android、Python、Java和Go,这个也是我们团队的主要技术栈。 Github:https://github.com/hylinux1024 微信公众号:终身开发者(angrycode)

问题

前面我们参考 EventBus 的实现逻辑模仿了一个最简单的 EasyBus。这个其实也是观察者模式的实现,所以整体逻辑理解起来应该是不难的。在 EasyBus 中进行注册的时候是通过反射机制对观察者的信息进行注册,然后解析出监听接口和事件类型( onEventXXXMethod(MessageEvent)方法以及其参数)。可以看出在这个注册的过程中,使用了运行时的反射机制,这在追求性能极致的基础组件中,这是可以优化的点。那么如何优化呢?这就需要用到今天提到的注解以及注解解析器的相关技术了。

注解

注解可以对包、类、接口方法、属性以及其它注解进行修饰,它是一个标记。就像所有的类继承于 Object一样,所有的注解本质都是继承于 Annotation这个接口。 如何定义注解呢? 这就需要使用元注解。元注解就是可以用来修饰其它注解的注解。 常见的元注解有

  • @Target 指定注解的作用目标。通过枚举 ElementType 来指定修饰包、类、方法、属性或者其它注解。
  • @Retention 指定注解的生命周期。同样通过枚举 RetentionPolicy 进行配置。生命周期从短到长为 RetentionPolicy.SOURCERetentionPolicy.CLASS, RetentionPolicy.RUNTIME 生命周期分别对应到源码文件、 Class文件以及运行时
  • @Documented 指定注解是否被包含在 JavaDoc
  • @Inherited 指定注解是否能被子类继承

例如常见的注解有 @Override@Deprecated@FuncationalInterface

看一下 @Override 的定义

@Target(ElementType.METHOD)
@Retention(RetentionPolicy.SOURCE)
public @interface Override {
}

该注解作用在方法中,并且生命周期是在编译期间就会被丢弃

再看一个 EventBus 中定义的注解

@Documented
@Retention(RetentionPolicy.RUNTIME)
@Target({ElementType.METHOD})
public @interface Subscribe {
    ThreadMode threadMode() default ThreadMode.POSTING;

    /**
     * If true, delivers the most recent sticky event (posted with
     * {@link EventBus#postSticky(Object)}) to this subscriber (if event available).
     */
    boolean sticky() default false;

    /** Subscriber priority to influence the order of event delivery.
     * Within the same delivery thread ({@link ThreadMode}), higher priority subscribers will receive events before
     * others with a lower priority. The default priority is 0. Note: the priority does *NOT* affect the order of
     * delivery among subscribers with different {@link ThreadMode}s! */
    int priority() default 0;
}

该注解作用于方法,且生命周期是最长的 Runtime 阶段,这个注解还定义了两个属性 stickypriority 分别表示这个事件是否是粘滞事件和事件的优先级。

注解解析器

EventBus 中默认是不使用注解解析器的,若要开启注解解析器生成索引辅助类,需要在 app/gradle 中配置 annotationProcessor

apply plugin: 'kotlin-kapt' // ensure kapt plugin is applied

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

kapt {
    arguments {
        arg('eventBusIndex', 'com.github.easybus.MyEventBusIndex')
    }
}

由于我的 Demo 使用 Kotlin 编写,所以配置的是 kapt。详细配置文档可以参考EventBus文档

通过 AS 编译之后,就会在 build/generated/source/kapt/debug/com/github/easybus目录下生成索引文件 MyEventBusIndex 这个文件名称就是在 kapt 中配置的。

打开 MyEventBusIndex 源码

/** 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>();

        putIndex(new SimpleSubscriberInfo(com.github.easybus.demo.MainActivity.class, true, new SubscriberMethodInfo[] {
            new SubscriberMethodInfo("onEventUpdate", com.github.easybus.demo.MessageEvent.class, ThreadMode.MAIN),
            new SubscriberMethodInfo("onEventUpdate2", com.github.easybus.demo.MessageEvent.class, ThreadMode.MAIN),
        }));

    }

    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 类型的索引,存储了订阅者的信息,这个就是编译时动态生成的辅助类。在 static 代码块中,将订阅者 MainActivity 的信息 onEventUpdateonEventUpdate2 封装到 SubscriberInfo 中。 然后就可以在项目中使用这个索引辅助类

EventBus eventBus = EventBus.builder().addIndex(new MyEventBusIndex()).build();

或者全局配置

EventBus.builder().addIndex(new MyEventBusIndex()).installDefaultEventBus();
// Now the default instance uses the given index. Use it like this:
EventBus eventBus = EventBus.getDefault();

通过它就可以避免在运行时对注解进行解析,使得基础组件的性能达到最佳。

总结

本文简单介绍了通过元注解自定义注解,以及在 EventBus 中是如何使用注解解析器提升性能。关于注解解析器的实现将在下一篇进行拆解。

引用

  • http://greenrobot.org/eventbus/documentation/subscriber-index/ EventBus 关于配置订阅者索引的官方文档

本文分享自微信公众号 - 终身开发者(AngryCode),作者:hylinux1024

原文出处及转载信息见文内详细说明,如有侵权,请联系 yunjia_community@tencent.com 删除。

原始发表时间:2019-12-10

本文参与腾讯云自媒体分享计划,欢迎正在阅读的你也加入,一起分享。

我来说两句

0 条评论
登录 后参与评论

相关文章

  • Python Web Flask源码解读(四)——全局变量

    Flask中全局变量有current_app、request、g和session。不过需要注意的是虽然标题是写着全局变量,但实际上这些变量都跟当前请求的上下文环...

    阳仔
  • Python Web Flask源码解读(一)——启动流程

    WebServerGatewayInterface 它由 Python标准定义的一套 WebServer与 WebApplication的接口交互规范。

    阳仔
  • 100行代码拆解EventBus核心逻辑(三)

    在前文的讲解中对 EventBus 的实现逻辑有了大概的理解之后,我们知道 Java 解析注解可以在运行时解析也可以在编译期间解析。由于运行时解析是通过反射来获...

    阳仔
  • Java创建Annotation

    注解是Java很强大的部分,但大多数时候我们倾向于使用而不是去创建注解。例如,在Java源代码里不难找到Java编译器处理的@Override注解,Spring...

    银河1号
  • 最通俗易懂的java注解讲解

    Tanyboye
  • 解读Java 注解 (Annotation)

    “ Annotation中文意思就是注解的意思,在 Java 中注解是一个很重要的知识点,但是理解Annotation以及怎么运用,你是否真的明白?”

    每天学Java
  • 自定义注解加AOP怎么玩?

    注解是在JDK1.5之后引入的新特性位于java.lang.annotation,注解其实就是对代码进行一种特殊的标记,这些标记可以在编译,类加载和运行时被读取...

    乱敲代码
  • JDK1.9-注解

    版权声明:本文为博主原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接和本声明...

    cwl_java
  • java必学核心知识总结——注解

    前几年我们的项目还在structs 2 上跑,有一次问一个同事是否知道Spring Boot,同事说那不是用注解来开发的吗。虽然这个答案并不完全对,但是从客观上...

    码农小胖哥
  • Java注解总结(史上最全,有这一篇就够了)

    Java 注解用于为 Java 代码提供元数据。作为元数据,注解不直接影响你的代码执行,但也有一些类型的注解实际上可以用于这一目的。Java 注解是从 Java...

    Java_老男孩

扫码关注云+社区

领取腾讯云代金券