zoukankan      html  css  js  c++  java
  • EventBus 及一些思考

    EventBus 是 Android 开发的一种常用框架,其解耦的思维令人赞叹

    从特性上来讲,其与 Android SDK中的BroadcastReceiver很像,二者都是注册,发送事件,反注册,都可以定义事件的优先级,且都支持粘性(sticky)事件,只是EventBus 使用起来简单得多,而且不能跨进程

    Android SDK其实也有一个不能跨进程的BroadcastReceiver机制——LocalBroadcastManager,其发送和接受的广播只能在本进程,相比传统的 registerBroadcastReceiver,其有着更高的安全性,与EventBus的相似度也更高

    关于 EventBus 基础,请参考:

    EventBus

    使用教程 高级用法 源码解析

    这里结合源码,记录几个在 EventBus 学习和使用中值得思考和注意的地方:

    注册

    1. 哪些方法会被注册 ?
    2. 多次注册同一对象会如何?
    3. 注册对象没有被 Subscribe 注解的方法会如何 ?
    4. 注册对象时父类中被 Subscribe 注解的方法会被注册吗?
    5. 如果 子类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 通过反射调用方法,自然也就是实现类的方法;

    事件触发

    1. post时,如果没有找到 eventType 对应的注册方法会如何?
    2. 事件的 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));
            }
        }
    }
    

    反注册

    1. 多次反注册同一对象,或者反注册一个未被注册过的对象会如何?
    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());
        }
    }
    
  • 相关阅读:
    JS 创建对象的几种方式
    JS跨域笔记
    HTML5随笔
    css3随笔
    CSS3最简洁的轮播图
    canvas createRadialGradient 用法
    git 初级
    Oracle数据库字符集与国家字符集
    连接Oracle 19c出现ORA-28040:没有匹配的验证协议
    Vmware workstation虚拟机导入到esxi虚拟机
  • 原文地址:https://www.cnblogs.com/jamesvoid/p/12387602.html
Copyright © 2011-2022 走看看