EventBus3 源码解析(个人理解) 之二。

如果还没有看过EventBus3 源码解析(个人理解) 之一 建议先看前面的。

这一篇主要是为了解决以下这两问题?

5.为什么听别人说eventbus3的运行效率貌似比原来的快了许多倍?

6. Eventbus 有哪些很好的设计?

对于第五个疑问解答之前,我想问下大家在用Eventbus 3的时候有没有听说过注解,编译时注解?
如果没有弄懂的话,可以先搜索一下网络上面的知识,弄清楚概念在继续。

Eventbus 使用注解的用法 可以看到
Eventbus 注解使用方法
我们这里主要讲解用法
源码中有这样一个类

这里写图片描述

这个就是编译时注解总要的地方。

想要了解编译时注解可以看到这篇文章。

hongyang大神的编译时注解

上面的博客看完,如果在看到下面的这行代码其实有一点点感觉了。
我们先来看代码。

@SupportedAnnotationTypes("org.greenrobot.eventbus.Subscribe")
@SupportedOptions(value = {"eventBusIndex", "verbose"})
public class EventBusAnnotationProcessor extends AbstractProcessor {
    public static final String OPTION_EVENT_BUS_INDEX = "eventBusIndex";
    public static final String OPTION_VERBOSE = "verbose";

    /** Found subscriber methods for a class (without superclasses). */
    private final ListMap<TypeElement, ExecutableElement> methodsByClass = new ListMap<>();
    private final Set<TypeElement> classesToSkip = new HashSet<>();

    private boolean writerRoundDone;
    private int round;
    private boolean verbose;

    @Override
    public SourceVersion getSupportedSourceVersion() {
        return SourceVersion.latest();
    }



    @Override
    public boolean process(Set<? extends TypeElement> annotations, RoundEnvironment env) {
        //略过。
}

上面代码就是获取在方法上面注解过的方法名称,参数,类等信息。

为什么要获取啦?

我们看到这下面的代码(两段代码)。

  private void createInfoIndexFile(String index) {
        BufferedWriter writer = null;
        try {
            JavaFileObject sourceFile = processingEnv.getFiler().createSourceFile(index);
            int period = index.lastIndexOf('.');
            String myPackage = period > 0 ? index.substring(0, period) : null;
            String clazz = index.substring(period + 1);
            writer = new BufferedWriter(sourceFile.openWriter());
            if (myPackage != null) {
                writer.write("package " + myPackage + ";\n\n");
            }
            writer.write("import org.greenrobot.eventbus.meta.SimpleSubscriberInfo;\n");
            writer.write("import org.greenrobot.eventbus.meta.SubscriberMethodInfo;\n");
            writer.write("import org.greenrobot.eventbus.meta.SubscriberInfo;\n");
            writer.write("import org.greenrobot.eventbus.meta.SubscriberInfoIndex;\n\n");
            writer.write("import org.greenrobot.eventbus.ThreadMode;\n\n");
            writer.write("import java.util.HashMap;\n");
            writer.write("import java.util.Map;\n\n");
            writer.write("/** This class is generated by EventBus, do not edit. */\n");
            writer.write("public class " + clazz + " implements SubscriberInfoIndex {\n");
            writer.write("    private static final Map<Class<?>, SubscriberInfo> SUBSCRIBER_INDEX;\n\n");
            writer.write("    static {\n");
            writer.write("        SUBSCRIBER_INDEX = new HashMap<Class<?>, SubscriberInfo>();\n\n");
            writeIndexLines(writer, myPackage);
            writer.write("    }\n\n");
            writer.write("    private static void putIndex(SubscriberInfo info) {\n");
            writer.write("        SUBSCRIBER_INDEX.put(info.getSubscriberClass(), info);\n");
            writer.write("    }\n\n");
            writer.write("    @Override\n");
            writer.write("    public SubscriberInfo getSubscriberInfo(Class<?> subscriberClass) {\n");
            writer.write("        SubscriberInfo info = SUBSCRIBER_INDEX.get(subscriberClass);\n");
            writer.write("        if (info != null) {\n");
            writer.write("            return info;\n");
            writer.write("        } else {\n");
            writer.write("            return null;\n");
            writer.write("        }\n");
            writer.write("    }\n");
            writer.write("}\n");
        } catch (IOException e) {
            throw new RuntimeException("Could not write source for " + index, e);
        } finally {
            if (writer != null) {
                try {
                    writer.close();
                } catch (IOException e) {
                    //Silent
                }
            }
        }
    }


    /**
     * 把捕获到的注解全部写入文件。 大写的牛逼。
     * @param writer
     * @param myPackage
     * @throws IOException
     */
    private void writeIndexLines(BufferedWriter writer, String myPackage) throws IOException {

   1.     for (TypeElement subscriberTypeElement : methodsByClass.keySet()) {
            if (classesToSkip.contains(subscriberTypeElement)) {
                continue;
            }

            String subscriberClass = getClassString(subscriberTypeElement, myPackage);


            if (isVisible(myPackage, subscriberTypeElement)) {
       2.         writeLine(writer, 2,
                        "putIndex(new SimpleSubscriberInfo(" + subscriberClass + ".class,",
                        "true,", "new SubscriberMethodInfo[] {");
                List<ExecutableElement> methods = methodsByClass.get(subscriberTypeElement);
                       3. writeCreateSubscriberMethods(writer, methods, "new SubscriberMethodInfo", myPackage);
                writer.write("        }));\n\n");
            } else {
                writer.write("        // Subscriber not visible to index: " + subscriberClass + "\n");
            }
        }
    }

1.看到第一个代码块是不是感觉把代码写入到某一个文件中去了,而且还把头文件(import XXX)都写入了。我也是第一次见人这么写。

在仔细观察一下第一行代码是不是没有发现具体写入的方法?

其实是有的 大家可以看到

writeIndexLines()

这个方法咯 也就是第二个片段代码,看到是不是像在写

订阅的某个类( " + subscriberClass + ".class,")
接收订阅方法的 方法名称,形参的集合。

List<ExecutableElement> methods = methodsByClass.get(subscriberTypeElement);
writeCreateSubscriberMethods(writer, methods, "new SubscriberMethodInfo", myPackage);
writer.write(" }));\n\n");

当然啦,还有一些判断是否符合标准的方法,我们就不介绍了。有兴趣可以看看啦。

有没有想一想,这些东西最后到底生成了什么东西?

那么我就来看看吧?

格式如下:
我们接收
——————–类
————————-方法和参数

WorkReportFragementDay

         `@Subscribe(threadMode = ThreadMode.ASYNC)
public void onEventMainThread(WorkEvent event) {`}

WorkReportFragementMonth

   /**
     * 必须要public  回调
     *
     * @param event
     */
    @Subscribe(threadMode = ThreadMode.BACKGROUND)
    public void onEventMainThread(WorkEvent event) {
        Toast.makeText(getActivity(), " WorkReportFragementMonth --- " + "time=" + event.getTime() + " Content" + event.getContent(), Toast.LENGTH_SHORT).show();
        Log.d("aa", "Month");
    }

    @Subscribe(threadMode = ThreadMode.ASYNC)
    public void onEventMainThread(String event) {

    }

WorkReportFragementWeek

   @Subscribe
    public void onEventMainThread(WorkEvent event) {
        Toast.makeText(getActivity(), " WorkReportFragementWeek --- " + "time=" + event.getTime() + " Content" + event.getContent(), Toast.LENGTH_SHORT).show();
        Log.d("aa", "Week");
    }

编译一下,会生成一个文件咯 叫 MyEventBusIndex

让我们看看 这个类吧!

package com.study.sangerzhong.studyapp;

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 MyEventBusIndex implements SubscriberInfoIndex {
    private static final Map<Class<?>, SubscriberInfo> SUBSCRIBER_INDEX;

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

        putIndex(new SimpleSubscriberInfo(com.tlp.WorkReportFragementDay.class, true, new SubscriberMethodInfo[] {
            new SubscriberMethodInfo("onEventMainThread", com.tlp.WorkEvent.class, ThreadMode.ASYNC),
        }));

        putIndex(new SimpleSubscriberInfo(com.tlp.BaseFragment.class, true, new SubscriberMethodInfo[] {
            new SubscriberMethodInfo("onEventMainThread", com.tlp.WorkEvent.class),
            new SubscriberMethodInfo("onEventMainThread", String.class),
        }));

        putIndex(new SimpleSubscriberInfo(com.tlp.WorkReportFragementWeek.class, true, new SubscriberMethodInfo[] {
            new SubscriberMethodInfo("onEventMainThread", com.tlp.WorkEvent.class),
        }));

        putIndex(new SimpleSubscriberInfo(com.tlp.WorkReportFragementMonth.class, true, new SubscriberMethodInfo[] {
            new SubscriberMethodInfo("onEventMainThread", com.tlp.WorkEvent.class, ThreadMode.BACKGROUND),
            new SubscriberMethodInfo("onEventMainThread", String.class, ThreadMode.ASYNC),
        }));

    }

是不是和上面 那两段代码有非常吻合的地方?

如果大家认真看我讲解一个都应该知道

findUsingInfo 这个方法 还有我说一段话

//如果在加速索引没有找到值得话,那就反射吧。
如果有兴趣的话,可以去看看咯

第五个问题就解决了
————– 在编译的时候都找到了,这个方法。并生成静态的类。
————– 当然比要使用的时候反射来的快咯。
(你的速度好快哦,快慢都是要分时候的咯。)

  1. 问题六:

6. Eventbus 有哪些很好的设计?

第一个:其实上面的也是一个。

第二个:
循环利用对象,避免对象过多创建销毁。
可以比作回收池。

   private FindState prepareFindState() {
        synchronized (FIND_STATE_POOL) {
            for (int i = 0; i < POOL_SIZE; i++) {
                FindState state = FIND_STATE_POOL[i];
                if (state != null) {
                    FIND_STATE_POOL[i] = null;
                    return state;
                }
            }
        }
        return new FindState();
    }

解释一下:
生成对象的时候:

循环查找find_state_pool池中 是否有对象存储,如果都没有的话创建一个,如果有的话拿出来使用,并且回收数组的对象。

如下:

 FindState findState = prepareFindState();   //  复用。

存储订阅的对象(重新利用)

 void initForSubscriber(Class<?> subscriberClass) {
            this.subscriberClass = clazz = subscriberClass;
            //记录是否包含父类的方法
            skipSuperClasses = false;
            subscriberInfo = null;
        }

释放资源

    // 获取释放,利用牛的一比啊。
    private List<SubscriberMethod> getMethodsAndRelease(FindState findState) {
        List<SubscriberMethod> subscriberMethods = new ArrayList<>(findState.subscriberMethods);
        findState.recycle();

        synchronized (FIND_STATE_POOL) {  // 判断 这个池 还有没有地方存储,如果有进行存储。
            for (int i = 0; i < POOL_SIZE; i++) {
                if (FIND_STATE_POOL[i] == null) {
                    FIND_STATE_POOL[i] = findState;
                    break;
                }
            }
        }
        return subscriberMethods;
    }

解释一下:
1.把获取到的订阅方法,放入ArrayList<> 集合,
2.销毁findstate存储的所有资源,但是不释放该对象。(循环使用)。
3.看回收池是否填满,在进行补充。

其实有一个点很重要但是没有对比。可能就显示不出来牛逼

我们可以看看 findState.recycle() 有多少。

   void recycle() {
            subscriberMethods.clear();
            anyMethodByEventType.clear();
            subscriberClassByMethodKey.clear();
            methodKeyBuilder.setLength(0);
            subscriberClass = null;
            clazz = null;
            skipSuperClasses = false;
            subscriberInfo = null;
        }

可以比较一下,如果把这个对象直接传递出去,会占用的内存是不是比一个ArrayList 多,而且其他的东西还没有用到。这样做目标明确,减少内存消耗。

可能这就是写代码的艺术吧。

感谢大家观看到这里来。也感谢自己终于写完了。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值