EventBus3 源码解析(个人理解) 之二。
2016-06-12 22:45
274 查看
如果还没有看过EventBus3 源码解析(个人理解) 之一 建议先看前面的。
这一篇主要是为了解决以下这两问题?
5.为什么听别人说eventbus3的运行效率貌似比原来的快了许多倍?
6. Eventbus 有哪些很好的设计?
对于第五个疑问解答之前,我想问下大家在用Eventbus 3的时候有没有听说过注解,编译时注解?
如果没有弄懂的话,可以先搜索一下网络上面的知识,弄清楚概念在继续。
Eventbus 使用注解的用法 可以看到
Eventbus 注解使用方法
我们这里主要讲解用法
源码中有这样一个类
这个就是编译时注解总要的地方。
想要了解编译时注解可以看到这篇文章。
hongyang大神的编译时注解
上面的博客看完,如果在看到下面的这行代码其实有一点点感觉了。
我们先来看代码。
上面代码就是获取在方法上面注解过的方法名称,参数,类等信息。
为什么要获取啦?
我们看到这下面的代码(两段代码)。
1.看到第一个代码块是不是感觉把代码写入到某一个文件中去了,而且还把头文件(import XXX)都写入了。我也是第一次见人这么写。
在仔细观察一下第一行代码是不是没有发现具体写入的方法?
其实是有的 大家可以看到
writeIndexLines()
这个方法咯 也就是第二个片段代码,看到是不是像在写
订阅的某个类(
接收订阅方法的 方法名称,形参的集合。
当然啦,还有一些判断是否符合标准的方法,我们就不介绍了。有兴趣可以看看啦。
有没有想一想,这些东西最后到底生成了什么东西?
那么我就来看看吧?
格式如下:
我们接收
——————–类
————————-方法和参数
WorkReportFragementDay
WorkReportFragementMonth
WorkReportFragementWeek
编译一下,会生成一个文件咯 叫 MyEventBusIndex
让我们看看 这个类吧!
是不是和上面 那两段代码有非常吻合的地方?
如果大家认真看我讲解一个都应该知道
findUsingInfo 这个方法 还有我说一段话
//如果在加速索引没有找到值得话,那就反射吧。
如果有兴趣的话,可以去看看咯
第五个问题就解决了
————– 在编译的时候都找到了,这个方法。并生成静态的类。
————– 当然比要使用的时候反射来的快咯。
(你的速度好快哦,快慢都是要分时候的咯。)
问题六:
6. Eventbus 有哪些很好的设计?
第一个:其实上面的也是一个。
第二个:
循环利用对象,避免对象过多创建销毁。
可以比作回收池。
解释一下:
生成对象的时候:
循环查找find_state_pool池中 是否有对象存储,如果都没有的话创建一个,如果有的话拿出来使用,并且回收数组的对象。
如下:
存储订阅的对象(重新利用)
释放资源
解释一下:
1.把获取到的订阅方法,放入ArrayList<> 集合,
2.销毁findstate存储的所有资源,但是不释放该对象。(循环使用)。
3.看回收池是否填满,在进行补充。
其实有一个点很重要但是没有对比。可能就显示不出来牛逼
我们可以看看 findState.recycle() 有多少。
可以比较一下,如果把这个对象直接传递出去,会占用的内存是不是比一个ArrayList 多,而且其他的东西还没有用到。这样做目标明确,减少内存消耗。
这一篇主要是为了解决以下这两问题?
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 多,而且其他的东西还没有用到。这样做目标明确,减少内存消耗。
可能这就是写代码的艺术吧。
感谢大家观看到这里来。也感谢自己终于写完了。相关文章推荐
- 关于回调函数的理解
- 九度OJ 1010
- JS 把 String 转成 new Date()型
- ListView的使用技巧集合(五)
- Quartz与Spring强强联手,定时任务实现更容易
- 双端队列(Double-ended Queue)
- 类型限定词
- mongoose的基本操作(二)
- 安卓系统按键映射修改
- Bootstrap之表格checkbox复选框全选
- 理解MVC模式
- python的if语句
- Linux运维(数据库专题)面试题
- Android-银联支付开发
- 无锁队列的实现
- 无锁队列的实现
- 无锁队列的实现
- 从iOS端Facebook分享的预览效果到Open Graph Protocol(开放内容协议)
- HDU 1597 find the nth digit
- Linux应用编程之时间编程