您的位置:首页 > 其它

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

2016-06-12 22:45 274 查看
如果还没有看过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 这个方法 还有我说一段话

//如果在加速索引没有找到值得话,那就反射吧。

如果有兴趣的话,可以去看看咯

第五个问题就解决了

————– 在编译的时候都找到了,这个方法。并生成静态的类。

————– 当然比要使用的时候反射来的快咯。

(你的速度好快哦,快慢都是要分时候的咯。)

问题六:

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 多,而且其他的东西还没有用到。这样做目标明确,减少内存消耗。

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

感谢大家观看到这里来。也感谢自己终于写完了。
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: