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

栏目: 编程工具 · 发布时间: 7年前

内容简介:当订阅者向由于把订阅记录内主要包括3个成员变量。

一、Subscription

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

由于把 Subscription 翻译为名词性的“订阅”后,和字面上动词性的“订阅”没法区分。所以本系列文章,把该词翻译为更贴切的名词“订阅记录”。这个词会将在后续文章继续沿用。

订阅记录内主要包括3个成员变量。 subscriber 表示订阅者类的实例, subscriberMethod 表示订阅者内部接受事件的方法,和表示订阅记录是否存活的 active 。

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

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();
    }
}

二、SubscriberMethod

具体到 Subscription 内部实现,里面还包含了 SubscriberMethod 。

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

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 http://code.google.com/p/android/issues/detail?id=7811#c6
            return methodString.equals(otherSubscriberMethod.methodString);
        } else {
            return false;
        }
    }

    private synchronized void checkMethodString() {
        if (methodString == null) {
            // Method.toString has more overhead, just take relevant parts of the method
            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 注解。从注解类可以看到 threadMode 、 sticky 、 priority 均能和 SubscriberMethod 类的数据成员匹配上。

@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 修饰且不指定参数即可

@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 过程的类扫描会抛出异常:

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

前文铺垫 Subscription 、 SubscriberMethod 、 Subscribe 注解,全是都是为了降低 SubscriberMethodFinder 类的理解难度。因为通过此类扫描订阅者方法的 Subscribe 注解,为每个订阅方法生成 SubscriberMethod ,构造出订阅记录 Subscription 。所有事件根据 Subscription 派到对应订阅者的订阅方法。

4.1 类签名

class SubscriberMethodFinder

4.2 常量

在较新的类文件中编译器可能会添加方法, 这些方法被称为桥梁或合成方法。 EventBus 同时忽略这两种方法。这些修饰符都不是 public 的,而是以 Java 类文件格式定义的:http://docs.oracle.com/javase/specs/jvms/se7/html/jvms-4.html#jvms-4.6-200-A.1

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

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

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

扫描订阅者和订阅方法后的缓存

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

FindState对象缓存池大小

private static final int POOL_SIZE = 4;

FindState对象缓存池

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

4.3 数据成员

private List<SubscriberInfoIndex> subscriberInfoIndexes;
private final boolean strictMethodVerification;
private final boolean ignoreGeneratedIndex;

4.4 构造方法

SubscriberMethodFinder(List<SubscriberInfoIndex> subscriberInfoIndexes, boolean strictMethodVerification,
                       boolean ignoreGeneratedIndex) {
    this.subscriberInfoIndexes = subscriberInfoIndexes;
    this.strictMethodVerification = strictMethodVerification;
    this.ignoreGeneratedIndex = ignoreGeneratedIndex;
}

4.5 findSubscriberMethods

在订阅者类内扫描订阅者方法,如果订阅者类没有目标方法直接抛出异常

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

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

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

private List<SubscriberMethod> findUsingInfo(Class<?> subscriberClass) {
    // 从缓存池获取findState
    FindState findState = prepareFindState();
    // 用订阅者类初始化findState
    findState.initForSubscriber(subscriberClass);
    while (findState.clazz != 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();
    }
    return getMethodsAndRelease(findState);
}

4.8 getMethodsAndRelease

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

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 实例,如果缓存池没有缓存的实例,则创建新实例

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

获取订阅者的信息

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;
            }
        }
    }
    return null;
}

4.11 findUsingReflectionInSingleClass

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)) {
                // 订阅者方法的形参数量不唯一
                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

清除缓存

static void clearCaches() {
    METHOD_CACHE.clear();
}

五、FindState

5.1 类信息

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

static class FindState

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

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

从 SubscriberMethodFinder 的 POOL_SIZE 常量可知缓存池大小为4。

private static final int POOL_SIZE = 4;

5.2 不可变成员

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

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 缓存实例后,都会把订阅者类的基本信息存入以下变量,作为后续操作的参考内容。

Class<?> subscriberClass;
Class<?> clazz;
boolean skipSuperClasses;
SubscriberInfo subscriberInfo;

5.4 initForSubscriber

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

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

5.5 recycle

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

void recycle() {
    subscriberMethods.clear();
    anyMethodByEventType.clear();
    subscriberClassByMethodKey.clear();
    methodKeyBuilder.setLength(0);
    subscriberClass = null;
    clazz = null;
    skipSuperClasses = false;
    subscriberInfo = null;
}

5.6 checkAdd

boolean checkAdd(Method method, Class<?> eventType) {
    // 2 level check: 1st level with event type only (fast), 2nd level with complete signature when required.
    // Usually a subscriber doesn't have methods listening to the same event type.
    // 检查同一个订阅者类内是有多个方法订阅相同事件
    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

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)) {
        // Only add if not already found in a sub class
        // 只有在子类中找不到时才添加
        return true;
    } else {
        // Revert the put, old class is further down the class hierarchy
        // 撤销插入,旧类是进一步向下的类层次结构
        subscriberClassByMethodKey.put(methodKey, methodClassOld);
        return false;
    }
}

5.8 moveToSuperclass

void moveToSuperclass() {
    if (skipSuperClasses) {
        clazz = null;
    } else {
        clazz = clazz.getSuperclass();
        // 获取类名
        String clazzName = clazz.getName();
        /** Skip system classes, this just degrades performance. */
        // 跳过系统类,这只会降低性能。
        if (clazzName.startsWith("java.") || clazzName.startsWith("javax.") || clazzName.startsWith("android.")) {
            clazz = null;
        }
    }
}

六、事件发送给订阅者

6.1 invokeSubscriber

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

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

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

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);
        }
    }
}

以上所述就是小编给大家介绍的《EventBus源码剖析(4) -- 订阅记录》,希望对大家有所帮助,如果大家有任何疑问请给我留言,小编会及时回复大家的。在此也非常感谢大家对 码农网 的支持!

查看所有标签

本站部分资源来源于网络,本站转载出于传递更多信息之目的,版权归原作者或者来源机构所有,如转载稿涉及版权问题,请联系我们。

大视觉创意宝典

大视觉创意宝典

2008-8 / 28.00元

《大视觉创意宝典:网页设计》主要内容:将优秀的网页分为设计、卡通、教育、金融、通讯、企业、房地产、娱乐等十四个章节,并详尽解析其页面布局、配色参考、设计特色及细节元素。丛书编写以设计基础的角度出发,具备速查、参照、案头工具书等功能。一起来看看 《大视觉创意宝典》 这本书的介绍吧!

JSON 在线解析
JSON 在线解析

在线 JSON 格式化工具

随机密码生成器
随机密码生成器

多种字符组合密码

html转js在线工具
html转js在线工具

html转js在线工具