EventBus源码剖析(4) -- 订阅记录

Posted by phantomVK on December 6, 2018

文章列表:

一、Subscription

订阅者进行注册时,EventBus 会扫描整个订阅者类,获取接收事件的具体方法,并构造出 Subscription 实例。每个订阅者可有多个方法接收订阅事件,每个方法生成各自的 Subscription 作为事件接收凭证。

订阅记录内主要包括3个成员变量:

  • subscriber表示订阅类的实例;
  • subscriberMethod表示订阅者内接受事件的方法;
  • 还有表示订阅记录是否有效的active
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
final class Subscription {
    // 订阅者类的实例
    final Object subscriber;
    
    // 订阅事件的方法
    final SubscriberMethod subscriberMethod;

    // 订阅记录是否依然生效标志位,volatile修饰控制多线程可见性
    volatile boolean active;

    // 构造方法,构造之后默认接受事件
    Subscription(Object subscriber, SubscriberMethod subscriberMethod) {
        this.subscriber = subscriber;
        this.subscriberMethod = subscriberMethod;
        active = true;
    }

    @Override
    public boolean equals(Object other) {
        if (other instanceof Subscription) {
            Subscription otherSubscription = (Subscription) other;
            // 对比是否为重复实例
            return subscriber == otherSubscription.subscriber
                    && subscriberMethod.equals(otherSubscription.subscriberMethod);
        } else {
            return false;
        }
    }

    @Override
    public int hashCode() {
        return subscriber.hashCode() + subscriberMethod.methodString.hashCode();
    }
}

调用 EventBus#unregister(Object) 注销订阅者后,active改为 false,该值由负责队列事件投递的 EventBus#invokeSubscriber(PendingPost) 检查以避免 race conditions

二、SubscriberMethod

具体到 Subscription 内部实现,里面还包含了 SubscriberMethod。每个 SubscriberMethod 表示一个订阅者类的订阅方法。

前文已经提到,订阅者类通过 EventBus 的注解修饰并因此能被 EventBus 发现。EventBus 通过注解处理器分析注解,和获取的订阅者方法信息构造为 SubscriberMethod 实例,成为订阅者信息的索引。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
public class SubscriberMethod {
    // 接收事件的方法实例
    final Method method;
    
    // 线程模式
    final ThreadMode threadMode;
    
    // 事件类型
    final Class<?> eventType;
    
    // 事件优先级
    final int priority;
    
    // 是否粘性事件
    final boolean sticky;
    
    // 此字符串用于提高比较效率
    String methodString;

    public SubscriberMethod(Method method, Class<?> eventType, ThreadMode threadMode, int priority, boolean sticky) {
        this.method = method;
        this.threadMode = threadMode;
        this.eventType = eventType;
        this.priority = priority;
        this.sticky = sticky;
    }
    
    // 比较两个订阅者方法
    @Override
    public boolean equals(Object other) {
        if (other == this) {
            return true;
        } else if (other instanceof SubscriberMethod) {
            checkMethodString();
            SubscriberMethod otherSubscriberMethod = (SubscriberMethod)other;
            otherSubscriberMethod.checkMethodString();
            // Don't use method.equals because of https://issuetracker.google.com/issues/36916580#c6
            return methodString.equals(otherSubscriberMethod.methodString);
        } else {
            return false;
        }
    }

    private synchronized void checkMethodString() {
        if (methodString == null) {
            // Method.toString开销较大,只需要取方法名
            StringBuilder builder = new StringBuilder(64);
            // 三元组:订阅者类、订阅方法、订阅消息类型
            builder.append(method.getDeclaringClass().getName());
            builder.append('#').append(method.getName());
            builder.append('(').append(eventType.getName());
            methodString = builder.toString();
        }
    }

    @Override
    public int hashCode() {
        return method.hashCode();
    }
}

三、Subscribe注解

这个就是 EventBus 注解。从注解类看到 threadModestickypriority 都能和 SubscriberMethod 类的数据成员匹配上。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
@Documented
@Retention(RetentionPolicy.RUNTIME)
@Target({ElementType.METHOD})
public @interface Subscribe {
    // 通过指定线程模式调起订阅者方法
    ThreadMode threadMode() default ThreadMode.POSTING;

    // 若是粘性事件,把粘性事件发送给订阅者
    boolean sticky() default false;

    // 方法接收事件的优先级,默认优先级是0
    // 在相同线程内,高优先级订阅者方法比低优先级方法更早接收事件
    // 此优先级不会影响不同线程模式中不同订阅者事件的派发
    int priority() default 0;
}

若注解方法无需修改默认条件,则使用 @Subscribe 时不指定参数即可

1
2
3
4
5
6
7
// 三个参数的设置
@Subscribe(threadMode = ThreadMode.MAIN, sticky = false, priority = 0)
fun onEventReceived(event: UserEvent) {
    val name = event.name
    val age = event.age
    Log.i(TAG, "Event received, Name: $name, Age: $age.")
}

如果实例没有可执行方法,注册到 EventBus 过程的类扫描会抛出异常:

1
java.lang.RuntimeException: Unable to start activity ComponentInfo{com.phantomvk.playground/com.phantomvk.playground.MainActivity}: org.greenrobot.eventbus.EventBusException: Subscriber class com.phantomvk.playground.MainActivity and its super classes have no public methods with the @Subscribe annotation

四、SubscriberMethodFinder

4.1 类签名

前文铺垫 SubscriptionSubscriberMethodSubscribe 注解,是为了降低理解 SubscriberMethodFinder 的难度。

1
class SubscriberMethodFinder 

通过扫描订阅者方法的 Subscribe 注解,为对应订阅方法生成 SubscriberMethod 实例,再构造出订阅记录 Subscription

4.2 常量

较新的类文件中编译器可能会添加额外方法, 这些方法被称为桥梁或合成方法。EventBus 同时忽略这两种方法。

这些修饰符都不和 public 使用,而是以Java类文件格式定义,详情请看:jvms-4.html#jvms-4.6-200-A.1

1
2
private static final int BRIDGE = 0x40;
private static final int SYNTHETIC = 0x1000;

忽略抽象方法、静态方法、桥接方法、合成方法

1
private static final int MODIFIERS_IGNORE = Modifier.ABSTRACT | Modifier.STATIC | BRIDGE | SYNTHETIC;

扫描订阅者和订阅方法后生成缓存

1
private static final Map<Class<?>, List<SubscriberMethod>> METHOD_CACHE = new ConcurrentHashMap<>();

FindState 对象缓存池大小

1
private static final int POOL_SIZE = 4;

FindState 对象缓存池

1
private static final FindState[] FIND_STATE_POOL = new FindState[POOL_SIZE];

4.3 数据成员

1
2
3
private List<SubscriberInfoIndex> subscriberInfoIndexes;
private final boolean strictMethodVerification;
private final boolean ignoreGeneratedIndex;

4.4 构造方法

1
2
3
4
5
6
SubscriberMethodFinder(List<SubscriberInfoIndex> subscriberInfoIndexes, boolean strictMethodVerification,
                       boolean ignoreGeneratedIndex) {
    this.subscriberInfoIndexes = subscriberInfoIndexes;
    this.strictMethodVerification = strictMethodVerification;
    this.ignoreGeneratedIndex = ignoreGeneratedIndex;
}

4.5 findSubscriberMethods

在订阅者类内扫描订阅者方法,如果订阅者类没有目标方法直接抛出异常。从缓存列表中查找缓存减少扫描时间,缓存命中就不需要从订阅者类中进行查找。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
List<SubscriberMethod> findSubscriberMethods(Class<?> subscriberClass) {
    // 根据订阅者类从缓存中获取订阅者方法
    List<SubscriberMethod> subscriberMethods = METHOD_CACHE.get(subscriberClass);
    if (subscriberMethods != null) {
        return subscriberMethods;
    }

    // ignoreGeneratedIndex在EventBusBuilder.ignoreGeneratedIndex为false
    if (ignoreGeneratedIndex) {
        // 通过反射的方式查找订阅者方法
        subscriberMethods = findUsingReflection(subscriberClass);
    } else {
        // 查找订阅者方法
        subscriberMethods = findUsingInfo(subscriberClass);
    }
    
    // 在订阅者中没有找到使用注解标注的公开方法
    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;
    }
}

4.6 findUsingReflection

通过反射查找订阅者的订阅方法

1
2
3
4
5
6
7
8
9
10
11
private List<SubscriberMethod> findUsingReflection(Class<?> subscriberClass) {
    // 从缓存池获取findState
    FindState findState = prepareFindState();
    // 用订阅者类初始化findState
    findState.initForSubscriber(subscriberClass);
    while (findState.clazz != null) {
        findUsingReflectionInSingleClass(findState);
        findState.moveToSuperclass();
    }
    return getMethodsAndRelease(findState);
}

4.7 findUsingInfo

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
private List<SubscriberMethod> findUsingInfo(Class<?> subscriberClass) {
    // 从缓存池获取findState
    FindState findState = prepareFindState();
    // 用订阅者类初始化findState
    findState.initForSubscriber(subscriberClass);
    // 遇到:"java."、"javax."、"android."的类名clazz会置空,并终止这个循环
    while (findState.clazz != null) {
        // 如果订阅者之前没有注册过,则以下返回值为null,需通过反射的方式查找
        findState.subscriberInfo = getSubscriberInfo(findState);
        if (findState.subscriberInfo != null) {
            SubscriberMethod[] array = findState.subscriberInfo.getSubscriberMethods();
            // 上面无差别订阅类中所有方法,下面需要遍历筛选出有效的订阅方法
            for (SubscriberMethod subscriberMethod : array) {
                if (findState.checkAdd(subscriberMethod.method, subscriberMethod.eventType)) {
                    findState.subscriberMethods.add(subscriberMethod);
                }
            }
        } else {
            findUsingReflectionInSingleClass(findState);
        }
        // 扫描目标类转移到父类上
        findState.moveToSuperclass();
    }
    // 返回List<SubscriberMethod>,释放FindState实例
    return getMethodsAndRelease(findState);
}

4.8 getMethodsAndRelease

findState 获取订阅者的订阅方法, findState 重置后放入缓存,最后返回订阅者方法列表

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
private List<SubscriberMethod> getMethodsAndRelease(FindState findState) {
    // 从findState获取所有订阅方法
    List<SubscriberMethod> subscriberMethods = new ArrayList<>(findState.subscriberMethods);
    // 重置findState
    findState.recycle();
    // 把findState放入缓存池中
    synchronized (FIND_STATE_POOL) {
        for (int i = 0; i < POOL_SIZE; i++) {
            // 找一个非空位置存放可缓存实例
            if (FIND_STATE_POOL[i] == null) {
                FIND_STATE_POOL[i] = findState;
                break;
            }
        }
    }
    // 返回findState中保存的subscriberMethods
    return subscriberMethods;
}

4.9 prepareFindState

从缓存池中获取 FindState 实例,如果缓存池没有缓存的实例则创建新实例

1
2
3
4
5
6
7
8
9
10
11
12
13
14
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();
}

4.10 getSubscriberInfo

获取订阅者的信息

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
private SubscriberInfo getSubscriberInfo(FindState findState) {
    // 首次注册的订阅者,以下条件判断为空
    if (findState.subscriberInfo != null && findState.subscriberInfo.getSuperSubscriberInfo() != null) {
        SubscriberInfo superclassInfo = findState.subscriberInfo.getSuperSubscriberInfo();
        if (findState.clazz == superclassInfo.getSubscriberClass()) {
            return superclassInfo;
        }
    }
    // 首次注册的订阅者,以下条件判断为空
    if (subscriberInfoIndexes != null) {
        for (SubscriberInfoIndex index : subscriberInfoIndexes) {
            SubscriberInfo info = index.getSubscriberInfo(findState.clazz);
            if (info != null) {
                return info;
            }
        }
    }
    // 首次注册的订阅者返回值为null
    return null;
}

4.11 findUsingReflectionInSingleClass

FindState.clazz 就是订阅者类。先把订阅者的方法收集成为列表,后逐个分析方法的注解。根据约束条件逐渐筛选出符合条件的方法,放入到 FindState.subscriberMethods

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
private void findUsingReflectionInSingleClass(FindState findState) {
    Method[] methods;
    try {
        // 此方法比getMethods速度更快,尤其是订阅者像Activities这种巨型类的时候
        methods = findState.clazz.getDeclaredMethods();
    } catch (Throwable th) {
        // Workaround for java.lang.NoClassDefFoundError, see https://github.com/greenrobot/EventBus/issues/149
        methods = findState.clazz.getMethods();
        findState.skipSuperClasses = true;
    }
    
    // 遍历订阅者类内所有方法
    for (Method method : methods) {
        int modifiers = method.getModifiers();
        // 方法使用可见性为public,且没有使用MODIFIERS_IGNORE修饰
        if ((modifiers & Modifier.PUBLIC) != 0 && (modifiers & MODIFIERS_IGNORE) == 0) {
            // 从方法获取变量类型
            Class<?>[] parameterTypes = method.getParameterTypes();
            // 变量的数量必须为1个,否则不处理
            if (parameterTypes.length == 1) {
                // 检查方法是否有Subscribe注解修饰
                Subscribe subscribeAnnotation = method.getAnnotation(Subscribe.class);
                if (subscribeAnnotation != null) {
                    // 从方法变量中确认订阅事件的类型
                    Class<?> eventType = parameterTypes[0];
                    if (findState.checkAdd(method, eventType)) {
                        // 从注解获取threadMode信息
                        ThreadMode threadMode = subscribeAnnotation.threadMode();
                        // 向findState增加新SubscriberMethod
                        findState.subscriberMethods.add(new SubscriberMethod(method, eventType, threadMode,
                                subscribeAnnotation.priority(), subscribeAnnotation.sticky()));
                    }
                }
            } else if (strictMethodVerification && method.isAnnotationPresent(Subscribe.class)) {
                // 订阅者方法的形参数量不为1,但又使用Subscribe注解修饰方法,需抛出异常
                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)) {
            // 方式包含Subscribe注解,但方法不能同时满足以下条件:公开可见性、非静态、非抽象
            String methodName = method.getDeclaringClass().getName() + "." + method.getName();
            throw new EventBusException(methodName +
                    " is a illegal @Subscribe method: must be public, non-static, and non-abstract");
        }
    }
}

4.12 clearCaches

清除缓存

1
2
3
static void clearCaches() {
    METHOD_CACHE.clear();
}

五、FindState

5.1 类信息

EventBus 创建多个缓存的 FindState 实例,当有订阅者需要扫描订阅方法时,从缓存池中取出一个 FindState ,向此实例中放入需被扫描的订阅者类。之后,包含订阅者的 FindState 传递给负责处理工作的方法,处理完毕后的订阅者方法结果也会存回到 FindState

1
static class FindState 

因此处理完毕后的 FindState 既包含订阅者类的信息,也保存着订阅者方法的列表。EventBusFindState 获取所有订阅者方法后,该 FindState 会重置并重新放入缓存池中。

根据 FindState 内部结构可知,FindState 实例包含 ArrayListHashMap、初始长度为128的 StringBuilder,所以 FindState 实例本身就有一定分量。如果实例不断创建并销毁,会加重虚拟机垃圾回收的负担。相反,把用过的 FindState 放入缓存池重用应该是更合理的行为。

SubscriberMethodFinderPOOL_SIZE 常量可知缓存池大小为4。

1
private static final int POOL_SIZE = 4;

5.2 不可变成员

这个数据成员用于保存订阅者类扫描扫描方法的结果,使用完毕后会简单清空为下次重用。

1
2
3
4
final List<SubscriberMethod> subscriberMethods = new ArrayList<>();
final Map<Class, Object> anyMethodByEventType = new HashMap<>();
final Map<String, Class> subscriberClassByMethodKey = new HashMap<>();
final StringBuilder methodKeyBuilder = new StringBuilder(128);

5.3 可变成员

这些成员用于存储订阅者类的信息。每次 EventBus 从缓存池获取 FindState 缓存实例后,都会把订阅者类的基本信息存入以下变量,作为后续操作的参考内容。

1
2
3
4
5
6
// 保存订阅者的类型
Class<?> subscriberClass;
Class<?> clazz;
boolean skipSuperClasses;
// 订阅者的信息,包含订阅者类的方法数组、给指向父类SubscriberInfo的引用
SubscriberInfo subscriberInfo;

5.4 initForSubscriber

订阅者类数据通过此方法设置到 FindState

1
2
3
4
5
void initForSubscriber(Class<?> subscriberClass) {
    this.subscriberClass = clazz = subscriberClass;
    skipSuperClasses = false;
    subscriberInfo = null;
}

5.5 recycle

FindState 使用完毕后需调用 recycle() 清空后归还给缓存池。这个方法的处理方式有点像 Message 类的 recycleUnchecked() 方法。

1
2
3
4
5
6
7
8
9
10
void recycle() {
    subscriberMethods.clear();
    anyMethodByEventType.clear();
    subscriberClassByMethodKey.clear();
    methodKeyBuilder.setLength(0);
    subscriberClass = null;
    clazz = null;
    skipSuperClasses = false;
    subscriberInfo = null;
}

5.6 checkAdd

两重检验:第一层,通过事件类型快速检验;第二层,在需要时通过完整的签名检查。一般来说,订阅者类不会有方法订阅同一个事件。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
boolean checkAdd(Method method, Class<?> eventType) {
    // 检查订阅者类内是否有多个方法订阅相同事件
    Object existing = anyMethodByEventType.put(eventType, method);
    if (existing == null) {
        // 有多个方法订阅相同事件
        return true;
    } else {
        if (existing instanceof Method) {
            if (!checkAddWithMethodSignature((Method) existing, eventType)) {
                throw new IllegalStateException();
            }
            // Put any non-Method object to "consume" the existing Method
            anyMethodByEventType.put(eventType, this);
        }
        return checkAddWithMethodSignature(method, eventType);
    }
}

5.7 checkAddWithMethodSignature

通过方法签名检查方法是否重复注册,检查通过就能加入到列表。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
private boolean checkAddWithMethodSignature(Method method, Class<?> eventType) {
    methodKeyBuilder.setLength(0);
    methodKeyBuilder.append(method.getName());
    methodKeyBuilder.append('>').append(eventType.getName());

    // 例如:onEventReceived>UserEvent
    String methodKey = methodKeyBuilder.toString();
    Class<?> methodClass = method.getDeclaringClass();
    // 通过插入新(methodKey, methodClass),并获取旧value
    Class<?> methodClassOld = subscriberClassByMethodKey.put(methodKey, methodClass);
    // 旧value为空,或methodClassOld是methodClass的父类或同类
    if (methodClassOld == null || methodClassOld.isAssignableFrom(methodClass)) {
        // 方法可以添加订阅
        return true;
    } else {
        // 撤销此次插入,保留缓存中的旧值
        subscriberClassByMethodKey.put(methodKey, methodClassOld);
        return false;
    }
}

5.8 moveToSuperclass

1
2
3
4
5
6
7
8
9
10
11
12
13
14
void moveToSuperclass() {
    if (skipSuperClasses) {
        clazz = null;
    } else {
        clazz = clazz.getSuperclass();
        // 获取类名
        String clazzName = clazz.getName();

        // 跳过系统类,这只会降低性能。
        if (clazzName.startsWith("java.") || clazzName.startsWith("javax.") || clazzName.startsWith("android.")) {
            clazz = null;
        }
    }
}

六、事件发送给订阅者

6.1 invokeSubscriber

根据 event 和 订阅记录 Subscription,就能把事件发送给订阅方法。

1
2
3
4
5
6
7
8
9
10
11
12
void invokeSubscriber(Subscription subscription, Object event) {
    try {
        // 用订阅者的实例和订阅的事件调起订阅方法
        subscription.subscriberMethod.method.invoke(subscription.subscriber, event);
    } catch (InvocationTargetException e) {
        // 订阅者方法接收事件是出现异常
        handleSubscriberException(subscription, event, e.getCause());
    } catch (IllegalAccessException e) {
        // 出现未知异常
        throw new IllegalStateException("Unexpected exception", e);
    }
}

6.2 handleSubscriberException

检查标志位决定是否处理事件订阅方法抛出的异常

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
private void handleSubscriberException(Subscription subscription, Object event, Throwable cause) {
    if (event instanceof SubscriberExceptionEvent) {
        if (logSubscriberExceptions) {
            // Don't send another SubscriberExceptionEvent to avoid infinite event recursion, just log
            logger.log(Level.SEVERE, "SubscriberExceptionEvent subscriber " + subscription.subscriber.getClass()
                    + " threw an exception", cause);
            SubscriberExceptionEvent exEvent = (SubscriberExceptionEvent) event;
            logger.log(Level.SEVERE, "Initial event " + exEvent.causingEvent + " caused exception in "
                    + exEvent.causingSubscriber, exEvent.throwable);
        }
    } else {
        if (throwSubscriberException) {
            throw new EventBusException("Invoking subscriber failed", cause);
        }
        if (logSubscriberExceptions) {
            logger.log(Level.SEVERE, "Could not dispatch event: " + event.getClass() + " to subscribing class "
                    + subscription.subscriber.getClass(), cause);
        }
        if (sendSubscriberExceptionEvent) {
            SubscriberExceptionEvent exEvent = new SubscriberExceptionEvent(this, cause, event,
                    subscription.subscriber);
            post(exEvent);
        }
    }
}