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()); } }
相关推荐
kururunga 2020-11-16
pengruiyu 2020-08-01
kururunga 2020-07-16
kururunga 2020-05-07
kururunga 2020-05-01
kururunga 2020-04-23
一个来自吉尔尼斯 2020-04-21
一个来自吉尔尼斯 2020-04-10
pengruiyu 2020-02-19
kururunga 2019-12-08
一个来自吉尔尼斯 2019-12-05
Androidtalent 2014-03-23
kuangren 2019-11-01
pengruiyu 2019-10-20
huangf 2015-04-17
kururunga 2015-05-17
huangf 2015-05-17
Androidtalent 2019-09-08