如果还没有看过EventBus3 源码解析(个人理解) 之一 建议先看前面的。
这一篇主要是为了解决以下这两问题?
5.为什么听别人说eventbus3的运行效率貌似比原来的快了许多倍?
6. Eventbus 有哪些很好的设计?
对于第五个疑问解答之前,我想问下大家在用Eventbus 3的时候有没有听说过注解,编译时注解?
如果没有弄懂的话,可以先搜索一下网络上面的知识,弄清楚概念在继续。
Eventbus 使用注解的用法 可以看到
Eventbus 注解使用方法
我们这里主要讲解用法
源码中有这样一个类
这个就是编译时注解总要的地方。
想要了解编译时注解可以看到这篇文章。
上面的博客看完,如果在看到下面的这行代码其实有一点点感觉了。
我们先来看代码。
@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 多,而且其他的东西还没有用到。这样做目标明确,减少内存消耗。
可能这就是写代码的艺术吧。
感谢大家观看到这里来。也感谢自己终于写完了。