EventBus源码解析(三)———EventBus的注册编译期获取订阅者信息

文章列表

EventBus源码解析(一)
EventBus源码解析(二)
EventBus源码解析(三)

前言

EventBus源码解析(一)中,我们着重介绍了基本使用方法,解析了EventBus的注册流程的源码。
EventBus源码解析(二)中,我们着重介绍了EventBus的发送事件的流程及源码是如何实现的。
今天,我们将介绍在EventBus3.0中引入的编译时注解及Subscriber Index,用法上略有不同。

Subscriber Index

这是EventBus3.0中一个可选的配置,虽然没有要求使用索引,但是相对比于不使用索引的情况下索引能可以提升Android的性能。
使用索引的先决条件是,有@Subscriber注解的方法才能为Subscriber和Event均为public的类生成索引,但是由于Java注解自身的处理,匿名类不能识别@Subscribe annotations。
当然如果不使用索引的话, EventBus会使用运行时反射机制来完成订阅事件查找,只是性能上会差一些。

如何使用

先说注解处理器的配置,在高于2.2.0版本的使用AnnotationProcessor,而低于2.2.0版本的是使用android-apt。由于我们使用的android gradle插件的版本是3.3.2,所以不介绍低于2.2.0版本一下的配置。
看一下如何配置

android {
    defaultConfig {
        javaCompileOptions {
            annotationProcessorOptions {
                arguments = [ eventBusIndex : 'com.source.wxl.sourcecodestudyapp.MyEventBusIndex' ]
            }
        }
    }
}
 
dependencies {
    implementation 'org.greenrobot:eventbus:3.1.1'
    annotationProcessor 'org.greenrobot:eventbus-annotation-processor:3.1.1'
}

使用annotationProcessor属性添加注解处理器,并添加注解处理器选项参数eventBusIndex用来指定生成索引类的全路径。所以我们在APP模块下的build.gradle文件中添加以上内容。然后rebuild工程。然后可以在/app/build/generated/source/apt/debug/你的包名目录下看到我们参数中指定的的类MyEventBusIndex
贴一下这个类的源码

/** This class is generated by EventBus, do not edit. */
public class MyEventBusIndex implements SubscriberInfoIndex {
	//注释1、表明此索引把订阅者类对象和订阅者信息的映射关系存储到一个map对象中,
    private static final Map<Class<?>, SubscriberInfo> SUBSCRIBER_INDEX;

    static {
        SUBSCRIBER_INDEX = new HashMap<Class<?>, SubscriberInfo>();
		//注释2、静态代码块中,将我们的订阅者的信息(包括订阅者类、是否在父类中查找、订阅方法的信息集合)封装到`SimpleSubscriberInfo`对象中,然后调用`putIndex`方法
        putIndex(new SimpleSubscriberInfo(com.source.wxl.sourcecodestudyapp.eventbus.ActivityA.class, true,
                new SubscriberMethodInfo[] {
            new SubscriberMethodInfo("onEvent", com.source.wxl.sourcecodestudyapp.eventbus.bean.MessageEvent.class),
            new SubscriberMethodInfo("onEvent2", com.source.wxl.sourcecodestudyapp.eventbus.bean.MessageEvent.class),
            new SubscriberMethodInfo("onReceiveMsg",
                    com.source.wxl.sourcecodestudyapp.eventbus.bean.MessageEvent.class, ThreadMode.MAIN),
        }));

        putIndex(new SimpleSubscriberInfo(com.source.wxl.sourcecodestudyapp.eventbus.BaseActivityA.class, true,
                new SubscriberMethodInfo[] {
            new SubscriberMethodInfo("onEventParent",
                    com.source.wxl.sourcecodestudyapp.eventbus.bean.MessageEvent.class),
        }));

    }

    private static void putIndex(SubscriberInfo info) {
    	//注释3、就是把注释2中传进来的`SimpleSubscriberInfo`对象put到变量`SUBSCRIBER_INDEX`中。
		//剩下那个方法就是获取`SimpleSubscriberInfo`对象了。
        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;
        }
    }
}

先简单看一下注解处理器自动生成的那个类MyEventBusIndex类的注释,看下都做了哪些工作。
接着看怎么使用Subscriber Index
在需要监听事件的类中,注册监听器,跟第一篇文章中讲到的注册方法有所不同。看一下怎么注册,用一个与前两篇文章不同的新例子

public class ActivityA extends BaseActivityA {
    private static final String TAG = "ActivityA=========a";
    TextView textView;
    @Override
    protected void onCreate(@Nullable Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_a);
        textView = findViewById(R.id.text);
        //注册
        //获取EventBusBuilder对象
        EventBusBuilder builder = EventBus.builder();
		//为builder对象的属性subscriberInfoIndexes(类型是List<SubscriberInfoIndex>)初始化并将MyEventBusIndex对象添加进list中,
		//并生成一个EventBus对象赋值给EventBus的defaultInstance变量中,当外部调用EventBus.getDefault();时就会把此对象返出来
        builder.addIndex(new MyEventBusIndex()).installDefaultEventBus();
        EventBus  eventBus = EventBus.getDefault();
        eventBus.register(this);
    }
    //省略订阅方法
    
}

ActivityA集成了BaseActivityA,把父类的代码贴一下

public class BaseActivityA extends AppCompatActivity {
    private static final String TAG = "BaseActivityA=========a";
	//省略无关方法...
    @Subscribe
    public void onEventParent(MessageEvent messageEvent){
        Log.d(TAG, "onEventParent: ");
    }
}

父类中也有一个订阅方法。
然后剩下的就跟第一种用法没有区别了。

源码分析

新的注册流程中,先获取EventBusBuilder对象,然后把编译时自动生成MyEventBusIndex对象添加进builder中然后installDefaultEventBus,最后getDefault获取默认的EventBus对象。
第一篇文章中我们提到EventBus对象中的属性是通过EventBusBuilder对象传进来,现在我们修改了Builder中的属性subscriberInfoIndexes的内容,并生成了默认的EventBus对象。
看一下EventBus的构造方法中的内容

EventBus(EventBusBuilder builder) {
    //省略部分代码
    indexCount = builder.subscriberInfoIndexes != null ? builder.subscriberInfoIndexes.size() : 0;
    subscriberMethodFinder = new SubscriberMethodFinder(builder.subscriberInfoIndexes,
            builder.strictMethodVerification, builder.ignoreGeneratedIndex);
    //省略部分代码
}

可以看到builder中的subscriberInfoIndexes属性影响了EventBus中的indexCountsubscriberMethodFinder两个变量,重点看一下第二个,因为我们是需要用SubscriberMethodFinder对象的帮助去遍历订阅者并查找订阅方法的,subscriberInfoIndexes最终会被传进SubscriberMethodFinder中,

SubscriberMethodFinder(List<SubscriberInfoIndex> subscriberInfoIndexes, boolean strictMethodVerification,
                           boolean ignoreGeneratedIndex) {
    this.subscriberInfoIndexes = subscriberInfoIndexes;
    this.strictMethodVerification = strictMethodVerification;
    this.ignoreGeneratedIndex = ignoreGeneratedIndex;
}

这样SubscriberMethodFindersubscriberInfoIndexes不为空,而且list的size=1(这唯一的数据就是在初始化EventBus的时候,调用builder的addIndex方法传进来的MyEventBusIndex的实例),还记不记得subscriberInfoIndexes变量在哪里用到过,在第一篇文章中出现过的方法,再贴一下相关方法

private List<SubscriberMethod> findUsingInfo(Class<?> subscriberClass) {
    FindState findState = prepareFindState();
    findState.initForSubscriber(subscriberClass);
    while (findState.clazz != null) {
    	//注释4
        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);
}

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

(注:这里对第一篇文章中的一个标记作一下解释,当时解析findSubscriberMethods方法的时候,代码的第七行加了一个注释标记 ,ignoreGeneratedIndex这个变量的判断,是对是否使用索引的判断,这个变量也是从builder中传进来的,默认是false,如果使用了索引并在builder中修改此变量为true,那就会强制使用反射的方式去查找订阅者了)
以上括号中的注只是对第一篇文章中存疑点的解释。
回到此方法中,在findUsingInfo中的注释4处会调用getSubscriberInfo方法,如果首次进来,findState.subscriberInfo为null,所以会跳过相关代码,进入下一个if判断,与第一篇文章中讲解的逻辑不同的是,subscriberInfoIndexes不为null,所以这次会走进这个分支,循环判断subscriberInfoIndexes变量,调用getSubscriberInfo(findState.clazz)方法,这样就会从myEventBusIndex对象的SUBSCRIBER_INDEX变量中拿出当前订阅者ActivityA的信息对象,也就是MyEventBusIndex类中静态代码块中的第一次调用putIndex时的对象SimpleSubscriberInfo,这个对象中,看一下这个类的代码

public class SimpleSubscriberInfo extends AbstractSubscriberInfo {

    private final SubscriberMethodInfo[] methodInfos;

    public SimpleSubscriberInfo(Class subscriberClass, boolean shouldCheckSuperclass, SubscriberMethodInfo[] methodInfos) {
    	//注意这里传的第二个参数为null,这个参数对应的是superSubscriberInfoClass-----注释5
        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;
    }
}

这样拿到SimpleSubscriberInfo这个对象后,返回到注释4处继续往下走,再接调用SimpleSubscriberInfogetSubscriberMethods方法,取出当前订阅者中订阅方法数组信息,接下来的逻辑就跟第一篇文章中讲到的逻辑差不多了,遍历订阅方法,二级检查并加入到findStatesubscriberMethods变量中,然后moveToSuperclass进入父类BaseActivityA
然后继续调用getSubscriberInfo方法,此时findState.subscriberInfo已经不为null了。看下第二个条件findState.subscriberInfo.getSuperSubscriberInfo(),因为findState.subscriberInfo其实就是SimpleSubscriberInfo对象,在这个对象的构造方法中调用父类的构造方法,传的第二个参数为null(看注释5),也就导致findState.subscriberInfo.getSuperSubscriberInfo()这个条件为null,所以getSubscriberInfo方法中的第一个条件判断中的逻辑还是不会执行,继续执行第二个条件判断,取出来的对象是MyEventBusIndex类静态代码块中的第二个putIndex方法放进去的SimpleSubscriberInfo对象,这样父类中的订阅者信息也拿到了,剩下的逻辑就跟第一次一样了。把取到的父类中的订阅者中的订阅方法放到findStatesubscriberMethods变量中。
这样所有的订阅方法就都取出来了,剩下的post事件的逻辑就是[第二篇文章]中的内容了

总结

第一篇文章核心思想是通过反射获取订阅者信息及订阅方法,而本篇文章讲的是在编译期就的到订阅者信息及订阅方法。在性能上此种方式有很大的提升,用官方的话就是在某些机型上性能提升了3倍之多。所以还是推荐使用此种方式。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值