【Android】EventBus 3.x使用APT提升性能

前言

EvenBus 2.x时代的消息订阅只能使用固定的方法名,比如:

onEvent
onEventMainThread
onEventBackgroundThread
onEventAsync

从EventBus 3.0开始支持使用注解来定义消息订阅者(subscriber),方法名是随意的,比如:

@Subscribe(threadMode = ThreadMode.MAIN)  
public void onMessageEvent(MessageEvent event) {
    
    
    // Do something
}

不过,EventBus3.0还有一个鲜为人知的功能,而且是一个可选的功能,默认是不带的,需要手动添加支持。

那就是使用APT在编译期间搜集所有的SubscribMethod。默认是在运行期间通过反射去搜集SubscriberMethod,这样性能就差点。

EventBus添加APT支持

在EventBus的github主页上有一个不起眼的地方,有提到建议使用APT(只是建议不是必须使用):
在这里插入图片描述
点击这个链接会跳转到单独介绍如何使用subscriber index(APT生成的代码)的指南页面:
https://greenrobot.org/eventbus/documentation/subscriber-index/

下面通过实践介绍如何使用subscriber index来提升EventBus的性能。

关于EventBus集成subscriber index很简单,先通过kapt额外添加eventbus-annotation-processor依赖:

dependencies {
    
    
 	implementation "org.greenrobot:eventbus:3.2.0"
 	kapt "org.greenrobot:eventbus-annotation-processor:3.2.0"
 }

然后给APT添加参数,注明生成subscriber index类的路径:

   defaultConfig {
    
    
        kapt {
    
    
           arguments {
    
    
               arg('eventBusIndex', 'com.devnn.bean.AppEventBusIndex')
           }
       }
   }

这个类是自动生成的,不需要手动去创建,命名可以随意。

build项目之后就会在build目录生成Java类:com.devnn.bean.AppEventBusIndex.java

然后需要在app启动时将这个类注册进EventBus中:

class MyApp :Application(){
    
    
    override fun onCreate() {
    
    
        super.onCreate()
        EventBus.builder().addIndex(AppEventBusIndex()).build()
    }
}

其它像以前一样正常使用EventBus即可。

关于APT有不了解的可以看笔者另一篇文章:
Google AutoService的使用与源码解析

下面分析这个类的作用。

EventBus APT流程分析

EventBus是通过建造者模式构建的,在EventBusBuilder类通过addIndex方法接收SubscriberInfoIndex对象:

    /** Adds an index generated by EventBus' annotation preprocessor. */
    public EventBusBuilder addIndex(SubscriberInfoIndex index) {
    
    
        if (subscriberInfoIndexes == null) {
    
    
            subscriberInfoIndexes = new ArrayList<>();
        }
        subscriberInfoIndexes.add(index);
        return this;
    }

而SubscriberInfoIndex是一个接口:

package org.greenrobot.eventbus.meta;

/**
 * Interface for generated indexes.
 */
public interface SubscriberInfoIndex {
    
    
    SubscriberInfo getSubscriberInfo(Class<?> subscriberClass);
}

EventBus源码中并没有任何关于这个接口的实现类,这个实现类就是需要通过我们去实现的。当然是不需要手动写代码去实现,而且通过apt自动帮我们生成。

在上述集成eventbus-annotation-processor步骤完成之后,build项目就会在build目录生成com.devnn.bean.AppEventBusIndex.java,它就是SubscriberInfoIndex接口的实现。

AppEventBusIndex完整代码如下:

package com.devnn.bean;

import org.greenrobot.eventbus.meta.SimpleSubscriberInfo;
import org.greenrobot.eventbus.meta.SubscriberMethodInfo;
import org.greenrobot.eventbus.meta.SubscriberInfo;
import org.greenrobot.eventbus.meta.SubscriberInfoIndex;

import org.greenrobot.eventbus.ThreadMode;

import java.util.HashMap;
import java.util.Map;

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

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

        putIndex(new SimpleSubscriberInfo(com.devnn.demo.MainActivity.class, true, new SubscriberMethodInfo[] {
    
    
            new SubscriberMethodInfo("onReceiveEvent", com.devnn.demo.MyEvent.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;
        }
    }
}

在MainActivity中的添加的subscriber方法如下:

    @Subscribe(threadMode = ThreadMode.MAIN)
    fun onReceiveEvent(event:MyEvent){
    
    
        Log.i("MainActivity","onReceiveEvent,msg=${
      
      event.msg}")
    }

在生成的AppEventBusIndex类中可以看到已经将onReceiveEvent方法保存起来了。

EventBus源码分析

EventBus的register方法会调用SubscriberMethodFinder这个类去搜集SubscriberMethod

//org.greenrobot.eventbus.EventBus.java
  public void register(Object subscriber) {
    
    
      Class<?> subscriberClass = subscriber.getClass();
      List<SubscriberMethod> subscriberMethods = subscriberMethodFinder.findSubscriberMethods(subscriberClass);
      synchronized (this) {
    
    
          for (SubscriberMethod subscriberMethod : subscriberMethods) {
    
    
              subscribe(subscriber, subscriberMethod);
          }
      }
  }

SubscriberMethod就是保存Method相关信息的类:

public class SubscriberMethod {
    
    
    final Method method;
    final ThreadMode threadMode;
    final Class<?> eventType;
    final int priority;
    final boolean sticky;
    //省略其它代码
  }

SubscriberMethodFinderfindSubscriberMethods方法如下:

//org.greenrobot.eventbus.SubscriberMethodFinder.java
    List<SubscriberMethod> findSubscriberMethods(Class<?> subscriberClass) {
    
    
        List<SubscriberMethod> subscriberMethods = METHOD_CACHE.get(subscriberClass);
        if (subscriberMethods != null) {
    
    
            return subscriberMethods;
        }

        if (ignoreGeneratedIndex) {
    
    
        	//如果强制使用反射就走这里 
        	//ignoreGeneratedIndex是在EventBusBuilder中设置的,默认是false
            subscriberMethods = findUsingReflection(subscriberClass);
        } else {
    
    
        	//默认走这里,优先查找subscrier index类
            subscriberMethods = findUsingInfo(subscriberClass);
        }
        if (subscriberMethods.isEmpty()) {
    
    
            throw new EventBusException("Subscriber " + subscriberClass
                    + " and its super classes have no public methods with the @Subscribe annotation");
        } else {
    
    
            METHOD_CACHE.put(subscriberClass, subscriberMethods);
            return subscriberMethods;
        }
    }

ignoreGeneratedIndex表示是否强制使用反射,它是在EventBusBuilder中设置的,默认是false,也就是会优先去查找SubscriberInfoIndex的实现类。

再看它的findUsingInfo方法:

//org.greenrobot.eventbus.SubscriberMethodFinder.java
  private List<SubscriberMethod> findUsingInfo(Class<?> subscriberClass) {
    
    
        FindState findState = prepareFindState();
        findState.initForSubscriber(subscriberClass);
        while (findState.clazz != null) {
    
    
        	//查找SubscriberMethod的关键代码:
            findState.subscriberInfo = getSubscriberInfo(findState);
            if (findState.subscriberInfo != null) {
    
    
            //通过apt已经找到了SubscriberMethod
                SubscriberMethod[] array = findState.subscriberInfo.getSubscriberMethods();
                for (SubscriberMethod subscriberMethod : array) {
    
    
                    if (findState.checkAdd(subscriberMethod.method, subscriberMethod.eventType)) {
    
    
                        findState.subscriberMethods.add(subscriberMethod);
                    }
                }
            } else {
    
    
            	//使用反射去查找SubscriberMethod
                findUsingReflectionInSingleClass(findState);
            }
            findState.moveToSuperclass();
        }
        return getMethodsAndRelease(findState);
    }

可以看到,关键代码:
findState.subscriberInfo = getSubscriberInfo(findState);

当subscriberInfo不为空就表示已经有SubscriberInfoIndex实现类,直接从这里面获取SubscriberInfo,如果不成立就用反射去查找。

SubscriberInfo里保存了SubscriberMethod的集合:

public interface SubscriberInfo {
    
    
    Class<?> getSubscriberClass();

    SubscriberMethod[] getSubscriberMethods();

    SubscriberInfo getSuperSubscriberInfo();

    boolean shouldCheckSuperclass();
}

然后看getSubscriberInfo方法实现:

//org.greenrobot.eventbus.SubscriberMethodFinder.java
    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这个集合中查到subscriberInfoIndex的实现类,而subscriberInfoIndex的实现类是apt自动生成的,并且通过EventBusBuilder的addIndex在app启动时添加到subscriberInfoIndexes中。这样也就避免了反射去查找subscriberInfo带来的性能问题。

猜你喜欢

转载自blog.csdn.net/devnn/article/details/127051135