Android EventBus使用优化

  前一篇文章 Android EventBus使用理解 最后就在怀疑反射查找订阅方法会影响效率,就在官网上看到了推荐使用Subscriber Index在APP产品中。给的理由是更快和避免崩溃。
  使用Subscriber Index避免用反射在运行时做昂贵的订阅方法的查找。它用注解处理器在编译时查找订阅方法。
  真的是很赞!在运行时的花销给放在编译时完成了。

满足要求

  • 订阅方法和订阅者类必须是public
  • 事件类必须是public
  • 订阅方法不能放到匿名类里面

如何生成index

  如果是Java语言,使用注解处理器。如果使用Kotlin,则使用Kapt
  这里主要讲使用Java的配置方式,由于我这个build.gradle.kts是用的Kotlin脚本,我贴出来我的配置

android {
    
        
    defaultConfig {
    
        
        javaCompileOptions {
    
    
            annotationProcessorOptions.arguments["eventBusIndex"] = "com.example.testeventbus.MyEventBusIndex"
        }
    }    
}
dependencies {
    
    
    val eventbus_version = "3.3.1"
    implementation("org.greenrobot:eventbus:$eventbus_version")
    annotationProcessor("org.greenrobot:eventbus-annotation-processor:$eventbus_version")   
}         

如何使用index

  首先需要构建一下项目。像我配置的,它会自动生成com.example.testeventbus.MyEventBusIndex类。
  在代码中初始化的地方,在调用EventBus.getDefault()之前,需要调用EventBus.builder().addIndex(new MyEventBusIndex()).installDefaultEventBus()。
  这样就可以了,其他的啥都不用动了。

结合代码分析一下index

  还是在之前那篇文章 Android EventBus使用理解 的那个例子的基础上分析一下。
  主要是分析一下现在查询订阅方法那块的逻辑。

增加的类

  使用注解自动生成的类是com.example.testeventbus.MyEventBusIndex,看一下它的实现:

public class MyEventBusIndex implements SubscriberInfoIndex {
    
    
    private static final Map<Class<?>, SubscriberInfo> SUBSCRIBER_INDEX;

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

        putIndex(new SimpleSubscriberInfo(MainActivity.class, true, new SubscriberMethodInfo[] {
    
    
            new SubscriberMethodInfo("onMessageEvent", MessageEvent.class, ThreadMode.MAIN),
            new SubscriberMethodInfo("onMessage", 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;
        }
    }
}

  该类继承了SubscriberInfoIndex ,实现了getSubscriberInfo(Class<?> subscriberClass)方法,同时我们看到在MyEventBusIndex 类的静态代码块中,将订阅方法的相关信息放入静态成员SUBSCRIBER_INDEX中。
  在这个例子中,SUBSCRIBER_INDEX的key为MainActivity.class,MainActivity则是订阅者对象。SUBSCRIBER_INDEX其实就是以订阅者类对象的Class作为key,值就是SubscriberInfo 实现类。在这,它的值是SimpleSubscriberInfo类对象。
  SimpleSubscriberInfo初始化的第三个参数是一个SubscriberMethodInfo数组,SubscriberMethodInfo是用来描述订阅方法的。例子中,我们声明了两个方法,所以这里会有两个数组。Android EventBus使用理解 里我们通过反射查找的就是这两个方法,现在直接就已经找到了这两个方法,这就是官网上说的将运行时查找,放在编译时查找的意思。

将生成的Index放进EventBus.getDefault()

  因为我们每次调用都是通过EventBus.getDefault()来实现相关的操作,所以我们需要把生成的index放进EventBus.getDefault()中它才能起作用。它就是通过EventBus.builder().addIndex(new MyEventBusIndex()).installDefaultEventBus()来完成的。
  先看EventBus.builder().addIndex(new MyEventBusIndex())

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

  可见,这是将新生成的MyEventBusIndex添加进成员变量subscriberInfoIndexes中了。subscriberInfoIndexes是ArrayList类型。
  再看installDefaultEventBus()方法:

    /**
     * Installs the default EventBus returned by {@link EventBus#getDefault()} using this builders' values. Must be
     * done only once before the first usage of the default EventBus.
     *
     * @throws EventBusException if there's already a default EventBus instance in place
     */
    public EventBus installDefaultEventBus() {
    
    
        synchronized (EventBus.class) {
    
    
            if (EventBus.defaultInstance != null) {
    
    
                throw new EventBusException("Default instance already exists." +
                        " It may be only set once before it's used the first time to ensure consistent behavior.");
            }
            EventBus.defaultInstance = build();
            return EventBus.defaultInstance;
        }
    }

    /** Builds an EventBus based on the current configuration. */
    public EventBus build() {
    
    
        return new EventBus(this);
    }    

  直接就build()生成EventBus.defaultInstance了。以后再调用EventBus.getDefault()就是得到EventBus.defaultInstance了。
  EventBus初始化关于index的代码:

    EventBus(EventBusBuilder builder) {
    
    
        logger = builder.getLogger();
        subscriptionsByEventType = new HashMap<>();
        typesBySubscriber = new HashMap<>();
        stickyEvents = new ConcurrentHashMap<>();
        mainThreadSupport = builder.getMainThreadSupport();
        mainThreadPoster = mainThreadSupport != null ? mainThreadSupport.createPoster(this) : null;
        backgroundPoster = new BackgroundPoster(this);
        asyncPoster = new AsyncPoster(this);
        indexCount = builder.subscriberInfoIndexes != null ? builder.subscriberInfoIndexes.size() : 0;
        subscriberMethodFinder = new SubscriberMethodFinder(builder.subscriberInfoIndexes,
                builder.strictMethodVerification, builder.ignoreGeneratedIndex);
        logSubscriberExceptions = builder.logSubscriberExceptions;
        logNoSubscriberMessages = builder.logNoSubscriberMessages;
        sendSubscriberExceptionEvent = builder.sendSubscriberExceptionEvent;
        sendNoSubscriberEvent = builder.sendNoSubscriberEvent;
        throwSubscriberException = builder.throwSubscriberException;
        eventInheritance = builder.eventInheritance;
        executorService = builder.executorService;
    }

  主要就是中间两句关于indexCount 和subscriberMethodFinder 的了。将Index集合设置到SubscriberMethodFinder类对象subscriberMethodFinder 之中。我们知道SubscriberMethodFinder是关于查找订阅方法的,查找时,是和subscriberInfoIndexes有关的,之前没有设置Index时,它都是空的,都跳过了。现在设置了值了,就要重新看一下那块查找订阅方法的逻辑了。

查找订阅方法

  我们不用重头开始看,直接跳到相关代码,就在subscriberMethodFinder 类的findUsingInfo(Class<?> subscriberClass)方法中:

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

  关键的改变在getSubscriberInfo(findState)这块,之前这块返回的findState.subscriberInfo为空,所以会走findUsingReflectionInSingleClass(findState),现在它返回不为空了,所以不用在走反射的查找方式了。
  现在就看一下getSubscriberInfo(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;
    }

  这个时候,findState.subscriberInfo为null,但是subscriberInfoIndexes 不为null了,因为我们赋值了。我们的例子来说,subscriberInfoIndexes里面有一个MyEventBusIndex对象。然后调用的就是它的getSubscriberInfo(Class<?> subscriberClass)方法。它返回的是一个SimpleSubscriberInfo对象。接着就在该对象返回了。
  返回到findUsingInfo()方法了,接着会调用SimpleSubscriberInfo的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;
    }

  SimpleSubscriberInfo里面现在有2个SubscriberMethodInfo ,现在需要将SubscriberMethodInfo 转化为SubscriberMethod,是调用的createSubscriberMethod()实现的。

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

  subscriberClass就订阅者类对象的Class,这里通过getDeclaredMethod()得到对应的Method,这样就能生成SubscriberMethod对象了。
  这样返回到findUsingInfo()方法里,就又开始通过findState.checkAdd()来判断是否能将订阅方法添加到结果集合中了,这个判断逻辑就在前一篇文章 Android EventBus使用理解 里了。
  现在理解了Index怎么替代通过反射查找订阅方法了吧。

猜你喜欢

转载自blog.csdn.net/q1165328963/article/details/132240392