EventBus 及一些思考
EventBus 是 Android 开发的一种常用框架,其解耦的思维令人赞叹
从特性上来讲,其与 Android SDK中的BroadcastReceiver很像,二者都是注册,发送事件,反注册,都可以定义事件的优先级,且都支持粘性(sticky)事件,只是EventBus 使用起来简单得多,而且不能跨进程
Android SDK其实也有一个不能跨进程的BroadcastReceiver机制——LocalBroadcastManager,其发送和接受的广播只能在本进程,相比传统的 registerBroadcastReceiver,其有着更高的安全性,与EventBus的相似度也更高
关于 EventBus 基础,请参考:
这里结合源码,记录几个在 EventBus 学习和使用中值得思考和注意的地方:
注册
- 哪些方法会被注册 ?
- 多次注册同一对象会如何?
- 注册对象没有被 Subscribe 注解的方法会如何 ?
- 注册对象时父类中被 Subscribe 注解的方法会被注册吗?
- 如果 子类B 重写了 父类A 的方法 fun,注册子类B 的对象会发生什么?调用时会调用哪个类的方法?
上述问题,我们需要分析 register方法:
public void register(Object subscriber) {
Class<?> subscriberClass = subscriber.getClass();
// 1. 找 subscriber 中被需要被注册的方法
List<SubscriberMethod> subscriberMethods = subscriberMethodFinder.findSubscriberMethods(subscriberClass);
synchronized (this) {
// 2. 注册这些方法
for (SubscriberMethod subscriberMethod : subscriberMethods) {
subscribe(subscriber, subscriberMethod);
}
}
}
先来看上面代码第一点;
List<SubscriberMethod> findSubscriberMethods(Class<?> subscriberClass) {
// 缓存中取
List<SubscriberMethod> subscriberMethods = METHOD_CACHE.get(subscriberClass);
if (subscriberMethods != null) {
return subscriberMethods;
}
if (ignoreGeneratedIndex) {
// 通过反射去找
subscriberMethods = findUsingReflection(subscriberClass);
} else {
// 通过 Subscriber Index 去找,这里如果没找到,也会通过反射去找
subscriberMethods = findUsingInfo(subscriberClass);
}
// 如果没有 找到被 Subscribe 注解的方法,抛出异常 ———— 问题 3
if (subscriberMethods.isEmpty()) {
throw new EventBusException("Subscriber " + subscriberClass
+ " and its super classes have no public methods with the @Subscribe annotation");
} else {
METHOD_CACHE.put(subscriberClass, subscriberMethods);
return subscriberMethods;
}
}
// 继续分析注册方法的查找过程findUsingReflection
private List<SubscriberMethod> findUsingReflection(Class<?> subscriberClass) {
FindState findState = prepareFindState();
findState.initForSubscriber(subscriberClass);
while (findState.clazz != null) {
findUsingReflectionInSingleClass(findState);
// 继续去 subscriberClass 的父类中找,但是这里有异常情况(几乎不会发生),往下看 ———— 问题 4
findState.moveToSuperclass();
}
return getMethodsAndRelease(findState);
}
// findState.moveToSuperclass();
void moveToSuperclass() {
if (skipSuperClasses) { // 异常情况,至于在哪里设置该标志位呢,继续往下看
clazz = null;
}
// ...
}
// 继续分析注册方法的查找过程 findUsingReflectionInSingleClass
private void findUsingReflectionInSingleClass(FindState findState) {
Method[] methods;
// 先通过getDeclaredMethods查找,再通过getMethods查找
try {
methods = findState.clazz.getDeclaredMethods();
} catch (Throwable th) {
methods = findState.clazz.getMethods();
// ...
// 问题 4 的异常情况,在 getDeclaredMethods 发生异常时,跳过父类的注册方法查找
findState.skipSuperClasses = true;
}
for (Method method : methods) {
int modifiers = method.getModifiers();
if ((modifiers & Modifier.PUBLIC) != 0 && (modifiers & MODIFIERS_IGNORE) == 0) {
Class<?>[] parameterTypes = method.getParameterTypes();
if (parameterTypes.length == 1) {
Subscribe subscribeAnnotation = method.getAnnotation(Subscribe.class);
if (subscribeAnnotation != null) {
Class<?> eventType = parameterTypes[0];
if (findState.checkAdd(method, eventType)) {
// 方法被注册的条件: public,被 Subscribe 注解,参数列表只有1个参数(也就是事件类型) ———— 问题 1
ThreadMode threadMode = subscribeAnnotation.threadMode();
findState.subscriberMethods.add(new SubscriberMethod(method, eventType, threadMode,
subscribeAnnotation.priority(), subscribeAnnotation.sticky()));
}
}
} else if (strictMethodVerification && method.isAnnotationPresent(Subscribe.class)) {
// 在设置了 strictMethodVerification 时,如果 Subscribe 注解的方法参数个数不是 1,抛出异常,默认 strictMethodVerification标志是false,可以通过 EventBusBuilder 设置
String methodName = method.getDeclaringClass().getName() + "." + method.getName();
throw new EventBusException("@Subscribe method " + methodName +
"must have exactly 1 parameter but has " + parameterTypes.length);
}
} else if (strictMethodVerification && method.isAnnotationPresent(Subscribe.class)) {
// 在设置了 strictMethodVerification 时,如果被 Subscribe 注解的方法不是 public,抛出异常
String methodName = method.getDeclaringClass().getName() + "." + method.getName();
throw new EventBusException(methodName +
" is a illegal @Subscribe method: must be public, non-static, and non-abstract");
}
}
}
再来看 register方法中的第2 点:
private void subscribe(Object subscriber, SubscriberMethod subscriberMethod) {
Class<?> eventType = subscriberMethod.eventType;
Subscription newSubscription = new Subscription(subscriber, subscriberMethod);
CopyOnWriteArrayList<Subscription> subscriptions = subscriptionsByEventType.get(eventType);
if (subscriptions == null) {
subscriptions = new CopyOnWriteArrayList<>();
subscriptionsByEventType.put(eventType, subscriptions);
} else {
// 已经注册过了该 subscriber,再次注册抛出异常 ———— 问题 2
if (subscriptions.contains(newSubscription)) {
throw new EventBusException("Subscriber " + subscriber.getClass() + " already registered to event "
+ eventType);
}
}
// ...
}
上述问题 5 ,代码中没写,但是其实在回答问题 4 时已经回答了,在寻找注册方法时,会注册父类的合格的方法,那么在post 调用时,调用的是哪个方法呢,EventBus 通过反射调用方法,自然也就是实现类的方法;
事件触发
- post时,如果没有找到 eventType 对应的注册方法会如何?
- 事件的 eventType 调用时符合多态吗?
private void postSingleEvent(Object event, PostingThreadState postingState) throws Error {
Class<?> eventClass = event.getClass();
boolean subscriptionFound = false;
// 设置了 eventInheritance 标志,查找 eventType 的 父类 和 父接口 的对应的事件类型,eventInheritance 标志默认为 true ———— 问题 2
if (eventInheritance) {
List<Class<?>> eventTypes = lookupAllEventTypes(eventClass);
int countTypes = eventTypes.size();
for (int h = 0; h < countTypes; h++) {
Class<?> clazz = eventTypes.get(h);
subscriptionFound |= postSingleEventForEventType(event, postingState, clazz);
}
} else {
subscriptionFound = postSingleEventForEventType(event, postingState, eventClass);
}
if (!subscriptionFound) {
// 没有找到对应 eventType 的注册方法,先打个日志
if (logNoSubscriberMessages) {
logger.log(Level.FINE, "No subscribers registered for event " + eventClass);
}
// 如果设置了 sendNoSubscriberEvent 标志,post 一个 NoSubscriberEvent 事件 ———— 问题 1
if (sendNoSubscriberEvent && eventClass != NoSubscriberEvent.class &&
eventClass != SubscriberExceptionEvent.class) {
post(new NoSubscriberEvent(this, event));
}
}
}
反注册
- 多次反注册同一对象,或者反注册一个未被注册过的对象会如何?
public synchronized void unregister(Object subscriber) {
List<Class<?>> subscribedTypes = typesBySubscriber.get(subscriber);
if (subscribedTypes != null) {
for (Class<?> eventType : subscribedTypes) {
unsubscribeByEventType(subscriber, eventType);
}
typesBySubscriber.remove(subscriber);
} else {
// 没找到,就打了个日志 ———— 问题 1
logger.log(Level.WARNING, "Subscriber to unregister was not registered before: " + subscriber.getClass());
}
}
EventBus 及一些思考的更多相关文章
- EventBus/EventQueue 再思考
EventBus/EventQueue 再思考 Intro 之前写过两篇文章,造轮子系列的 EventBus/EventQueue,回想起来觉得当前的想法有点问题,当时对 EvenStore 可能有点 ...
- Amazing 2020
Amazing 2020 Intro 2020 转眼即逝,2020 是比较艰辛的一年,因为疫情原因,很多人的工作以及生活都多多少少受到了一些影响. 引用网上盛传的一句话--2020 实"鼠& ...
- 【Bugly干货分享】老司机教你 “飙” EventBus 3
Bugly 技术干货系列内容主要涉及移动开发方向,是由 Bugly 邀请腾讯内部各位技术大咖,通过日常工作经验的总结以及感悟撰写而成,内容均属原创,转载请标明出处. EventBus对于Android ...
- EventBus初理解
缘由: 平时工作,因为懒于动笔的原因,也没注重技术和经验的积累,导致之前曾经研究过的问题现在又忘记了,所以要慢慢注重积累,那么就从写作开始,谈谈对工作中碰到的问题进行整理和归纳. 我们 ...
- 有关Android插件化思考
最近几年移动开发业界兴起了「 插件化技术 」的旋风,各个大厂都推出了自己的插件化框架,各种开源框架都评价自身功能优越性,令人目不暇接.随着公司业务快速发展,项目增多,开发资源却有限,如何能在有限资源内 ...
- Android事件总线EventBus详解
顾名思义,AndroidEventBus是一个Android平台的事件总线框架,它简化了Activity.Fragment.Service等组件之间的交互,很大程度上降低了它们之间的耦合,使我们的代码 ...
- android EventBus详解(一)
EventBus 是一款针对Android优化的发布/订阅事件总线.主要功能是替代Intent, Handler, BroadCast 在 Fragment,Activity,Service,线程之间 ...
- eShopOnContainers 知多少[5]:EventBus With RabbitMQ
1. 引言 事件总线这个概念对你来说可能很陌生,但提到观察者(发布-订阅)模式,你也许就很熟悉.事件总线是对发布-订阅模式的一种实现.它是一种集中式事件处理机制,允许不同的组件之间进行彼此通信而又不需 ...
- EventBus 3.0源码解析
现在网上讲解EventBus的文章大多数都是针对2.x版本的,比较老旧,本篇文章希望可以给大家在新版本上面带来帮助. EventBus 是专门为Android设计的用于订阅,发布总线的库,用到这个库的 ...
随机推荐
- python代码技术优化
numba 编译优化 from numba import jit @jit def eval_mcc(y_true, y_prob, threshold=False): idx = np.argsor ...
- D. New Year and Conference(区间交,线段树)
题:https://codeforces.com/contest/1284/problem/D 题意:给定n个1对的时间断,我是这么理解的,甲去参加a时间段的讲座,乙去参加b时间段的讲座,然后若这n对 ...
- RDD(十)——案例实操
需求: 数据结构:时间戳,省份,城市,用户,广告,中间字段使用空格分割. 样本如下: 1516609143867 6 7 64 16 1516609143869 9 4 75 18 151660914 ...
- spring+mybatis配置多个数据源
http://www.cnblogs.com/lzrabbit/p/3750803.html
- HDU-1540 Tunnel Warfare(区间连续点长度)
http://acm.hdu.edu.cn/showproblem.php?pid=1540 Time Limit: 4000/2000 MS (Java/Others) Memory Limi ...
- ios Alamofire网络插件的使用
pod 'Alamofire' import Alamofire let headers:HTTPHeaders = [ "aa":"bb" ] let par ...
- win10//ubuntu安装tensorflow-gpu与kears,并用minist测试
WIn10 安装cuda 先安装VS,然后根据自己的版本安装CUDA. 安装完后,打开cmd命令行输入nvcc -V,检测是否安装成功 安装cuDDN 安装对应版本,解压后覆盖到CUDA的地址,默认为 ...
- SPI以及IIC的verilog实现以及两者之间的对比
一.SPI是一种常用的串行通信接口,与UART不同的地方在于.SPI可以同时挂多个从机,但是UART只能点对点的传输数据,此外SPI有四条线实现数据的传输,而UART采用的是2条实现串行数据的传输 1 ...
- Horizon公司环境
- [Windows] Windows API 串口通信
Windows 中通过Windows API 进行串口通信主要有以下步骤: 打开串口 配置串口 读写串口 关闭串口 打开串口 关键API: CreateFile Windows 中进行设备的操作,第一 ...