前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >EventBus源码解读

EventBus源码解读

原创
作者头像
笔头
修改2022-01-21 10:47:55
3630
修改2022-01-21 10:47:55
举报
文章被收录于专栏:Android记忆Android记忆

EventBus是一个开源库,它利用发布/订阅者者模式来对项目进行解耦,达到实现多组件间通信效果。大概流程是注册订阅者-> 生产者POST 事件 -> 订阅者消费

一、Register

主要有2个注意点,findSubscriberMethods函数以及subscribe函数

代码语言:txt
复制
public void register(Object subscriber) {
    if (AndroidDependenciesDetector.isAndroidSDKAvailable() && !AndroidDependenciesDetector.areAndroidComponentsAvailable()) {
        // Crash if the user (developer) has not imported the Android compatibility library.
        throw new RuntimeException("It looks like you are using EventBus on Android, " +
                "make sure to add the \"eventbus\" Android library to your dependencies.");
    }
    //-------------------------------------------------------1
    Class<?> subscriberClass = subscriber.getClass();
    //-------------------------------------------------------2
    List<SubscriberMethod> subscriberMethods = subscriberMethodFinder.findSubscriberMethods(subscriberClass);
    synchronized (this) {
        for (SubscriberMethod subscriberMethod : subscriberMethods) {
            //------------------------------------------------3
            subscribe(subscriber, subscriberMethod);
        }
    }
}

1.获取当前订阅者

2.获取当前类中被@Subscriber注解的方法即订阅方法,详情请看findSubscriberMethods()

3.遍历所有的订阅方法,订阅事件存放到subscriptionsByEventType、订阅内容类型存放在typesBySubscriber中,详情查看subscribe()

一、findSubscriberMethods

代码语言:txt
复制
List<SubscriberMethod> findSubscriberMethods(Class<?> subscriberClass) {
    //------------------------------------------------------1
    List<SubscriberMethod> subscriberMethods = METHOD_CACHE.get(subscriberClass);
    if (subscriberMethods != null) {
        return subscriberMethods;
    }

   //---------------------------------------------------------2
    if (ignoreGeneratedIndex) {
        subscriberMethods = findUsingReflection(subscriberClass);
    } else {
    //--------------------------------------------------------3
        subscriberMethods = findUsingInfo(subscriberClass);
    }
    if (subscriberMethods.isEmpty()) {
        throw new EventBusException("Subscriber " + subscriberClass
                + " and its super classes have no public methods with the @Subscribe annotation");
    } else {
    //--------------------------------------------------------4
        METHOD_CACHE.put(subscriberClass, subscriberMethods);
        return subscriberMethods;
    }
}

1.从缓存中获取订阅方法

2.ignoreGeneratedIndex默认false,如果true,则通过反射查询订阅方法,详情看findUsingReflectionInSingleClass()

4.把订阅方法添加到METHOD_CACHE缓存中。

代码语言:txt
复制
private void findUsingReflectionInSingleClass(FindState findState) {
    Method[] methods;
    try {
        // ----------------------------------------------1
        methods = findState.clazz.getDeclaredMethods();
    } catch (Throwable th) {
        // Workaround for java.lang.NoClassDefFoundError, see https://github.com/greenrobot/EventBus/issues/149
        try {
            methods = findState.clazz.getMethods();
        } catch (LinkageError error) { // super class of NoClassDefFoundError to be a bit more broad...
            String msg = "Could not inspect methods of " + findState.clazz.getName();
            if (ignoreGeneratedIndex) {
                msg += ". Please consider using EventBus annotation processor to avoid reflection.";
            } else {
                msg += ". Please make this class visible to EventBus annotation processor to avoid reflection.";
            }
            throw new EventBusException(msg, error);
        }
        findState.skipSuperClasses = true;
    }
    //---------------------------------------------------2
    for (Method method : methods) {
    //---------------------------------------------------3
        int modifiers = method.getModifiers();
        //-----------------------------------------------4
        if ((modifiers & Modifier.PUBLIC) != 0 && (modifiers & MODIFIERS_IGNORE) == 0) {
        //-----------------------------------------------5
            Class<?>[] parameterTypes = method.getParameterTypes();
            if (parameterTypes.length == 1) {
                //---------------------------------------6
                Subscribe subscribeAnnotation = method.getAnnotation(Subscribe.class);
                if (subscribeAnnotation != null) {
                    Class<?> eventType = parameterTypes[0];
                    if (findState.checkAdd(method, eventType)) {
                        ThreadMode threadMode = subscribeAnnotation.threadMode();
                        //-------------------------------7
                        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)) {
            String methodName = method.getDeclaringClass().getName() + "." + method.getName();
            throw new EventBusException(methodName +
                    " is a illegal @Subscribe method: must be public, non-static, and non-abstract");
        }
    }
}

1.查找注册对象的所有方法

3.获取该方法的修饰符,即public、private等

4.只查看修饰符是public的方法

5.返回该方法的参数类型

6.只查看被@subscribe注解的方法,即订阅方法。

7.把订阅方法添加到FindState集合中

二、subscribe

上面findSubscriberMethods查找到所有订阅的方法,接下来需要对这些订阅方法处理,比如订阅方法不能存在重复、按照订阅内容类型为key把订阅事件按照优先级存放到subscriptionsByEventType中、订阅内容类型存放在typesBySubscriber中,后期在unregister中使用。

代码语言:txt
复制
private void subscribe(Object subscriber, SubscriberMethod subscriberMethod) {
    //-------------------------------------------------------1
    Class<?> eventType = subscriberMethod.eventType;
    //-------------------------------------------------------2
    Subscription newSubscription = new Subscription(subscriber, subscriberMethod);
    //-------------------------------------------------------3
    CopyOnWriteArrayList<Subscription> subscriptions = subscriptionsByEventType.get(eventType);
    //-------------------------------------------------------4
    if (subscriptions == null) {
        subscriptions = new CopyOnWriteArrayList<>();
        subscriptionsByEventType.put(eventType, subscriptions);
    } else {
    
        if (subscriptions.contains(newSubscription)) {
            throw new EventBusException("Subscriber " + subscriber.getClass() + " already registered to event "
                    + eventType);
        }
    }

    //--------------------------------------------------------5
    int size = subscriptions.size();
    for (int i = 0; i <= size; i++) {
        if (i == size || subscriberMethod.priority > subscriptions.get(i).subscriberMethod.priority) {
            subscriptions.add(i, newSubscription);
            break;
        }
    }

    //-------------------------------------------------------6
    List<Class<?>> subscribedEvents = typesBySubscriber.get(subscriber);
    if (subscribedEvents == null) {
        subscribedEvents = new ArrayList<>();
        typesBySubscriber.put(subscriber, subscribedEvents);
    }
    subscribedEvents.add(eventType);

    if (subscriberMethod.sticky) {
        if (eventInheritance) {
            // Existing sticky events of all subclasses of eventType have to be considered.
            // Note: Iterating over all events may be inefficient with lots of sticky events,
            // thus data structure should be changed to allow a more efficient lookup
            // (e.g. an additional map storing sub classes of super classes: Class -> List<Class>).
            Set<Map.Entry<Class<?>, Object>> entries = stickyEvents.entrySet();
            for (Map.Entry<Class<?>, Object> entry : entries) {
                Class<?> candidateEventType = entry.getKey();
                if (eventType.isAssignableFrom(candidateEventType)) {
                    Object stickyEvent = entry.getValue();
                    checkPostStickyEventToSubscription(newSubscription, stickyEvent);
                }
            }
        } else {
            Object stickyEvent = stickyEvents.get(eventType);
            checkPostStickyEventToSubscription(newSubscription, stickyEvent);
        }
    }
}

1.获得事件EventType,比如是String或者自定义的对象

2.根据订阅者和订阅方法构造一个订阅事件

3.根据EventType找到现有的所有订阅事件,

5.遍历所有subscriber相同EventType订阅事件,这里判断当前subscriberMethod订阅方法的优先级是否是大于集合中的subscriberMethod的优先级,如果是,把newSubscription插进去,这也表明了subscription中priority大的在前,这样在事件分发时就会先获取。

6.把当前订阅者中所有订阅方法内容类型存放到typesBySubscriber中,后期在unregister会使用,详情查看UnRegister

二、Post

代码语言:txt
复制
public void post(Object event) {
    //---------------------------------------------------------1
    PostingThreadState postingState = currentPostingThreadState.get();
    List<Object> eventQueue = postingState.eventQueue;
    eventQueue.add(event);
    //----------------------------------------------------------2
    if (!postingState.isPosting) {
        postingState.isMainThread = isMainThread();
        postingState.isPosting = true;
        if (postingState.canceled) {
            throw new EventBusException("Internal error. Abort state was not reset");
        }
        try {
            while (!eventQueue.isEmpty()) {
            //------------------------------------------------2
                postSingleEvent(eventQueue.remove(0), postingState);
            }
        } finally {
            postingState.isPosting = false;
            postingState.isMainThread = false;
        }
    }
}

1.PostingThreadState保存着事件队列和线程状态信息

2.这里用了ThreadLocal技术方案,在不同线程中,post不相互干扰,如果在同一线程中,订阅方法处理慢,eventQueue一直有数据,那就不走这步,订阅内容直接存放队列就好了。不同线程中的eventQueue,不相互影响。

2.遍历post单个订阅内容,详情查看postSingleEvent()

一、postSingleEvent

代码语言:txt
复制
private void postSingleEvent(Object event, PostingThreadState postingState) throws Error {
    Class<?> eventClass = event.getClass();
    boolean subscriptionFound = false;
    //------------------------------------------------------1
    if (eventInheritance) {
        List<Class<?>> eventTypes = lookupAllEventTypes(eventClass);
        int countTypes = eventTypes.size();
        for (int h = 0; h < countTypes; h++) {
            Class<?> clazz = eventTypes.get(h);
            //----------------------------------------------2
            subscriptionFound |= postSingleEventForEventType(event, postingState, clazz);
        }
    } else {
        subscriptionFound = postSingleEventForEventType(event, postingState, eventClass);
    }
    if (!subscriptionFound) {
        if (logNoSubscriberMessages) {
            logger.log(Level.FINE, "No subscribers registered for event " + eventClass);
        }
        if (sendNoSubscriberEvent && eventClass != NoSubscriberEvent.class &&
                eventClass != SubscriberExceptionEvent.class) {
            post(new NoSubscriberEvent(this, event));
        }
    }
}

1.eventInheritance默认是true,即获取当前的Event的Class对象的所有父类的Class对象集合,优先从缓存里读取,详情查看lookupAllEventTypes()

2.postSingleEventForEventType 详情查看postSingleEventForEventType方法

3.没有找到subscription订阅事件的情况下会抛错No subscribers registered for event或者sendNoSubscriberEvent不为空的情况则发送NoSubscriberEvent事件。

一、lookupAllEventTypes

遍历订阅内容及其父类,存放到eventTypesCache中。

代码语言:javascript
复制
private static List<Class<?>> lookupAllEventTypes(Class<?> eventClass) {
    synchronized (eventTypesCache) {
        List<Class<?>> eventTypes = eventTypesCache.get(eventClass);
        if (eventTypes == null) {
            eventTypes = new ArrayList<>();
            Class<?> clazz = eventClass;
            while (clazz != null) {
                eventTypes.add(clazz);
                addInterfaces(eventTypes, clazz.getInterfaces());
                clazz = clazz.getSuperclass();
            }
            eventTypesCache.put(eventClass, eventTypes);
        }
        return eventTypes;
    }
}

二、postSingleEventForEventType

遍历订阅事件,把订阅事件存放在threadLocal中,同时把事件分发出去

代码语言:txt
复制
private boolean postSingleEventForEventType(Object event, PostingThreadState postingState, Class<?> eventClass) {
    CopyOnWriteArrayList<Subscription> subscriptions;
    synchronized (this) {
        subscriptions = subscriptionsByEventType.get(eventClass);
    }
    if (subscriptions != null && !subscriptions.isEmpty()) {
        for (Subscription subscription : subscriptions) {
            postingState.event = event;
            postingState.subscription = subscription;
            boolean aborted;
            try {
                postToSubscription(subscription, event, postingState.isMainThread);
                aborted = postingState.canceled;
            } finally {
                postingState.event = null;
                postingState.subscription = null;
                postingState.canceled = false;
            }
            if (aborted) {
                break;
            }
        }
        return true;
    }
    return false;
}
代码语言:txt
复制
private void postToSubscription(Subscription subscription, Object event, boolean isMainThread) {
      //---------------------------------------------1
    switch (subscription.subscriberMethod.threadMode) {
        case POSTING:
            //----------------------------------------2
            invokeSubscriber(subscription, event);
            break;
        case MAIN:
            if (isMainThread) {
                invokeSubscriber(subscription, event);
            } else {
            //-----------------------------------------3
                mainThreadPoster.enqueue(subscription, event);
            }
            break;
        case MAIN_ORDERED:
            if (mainThreadPoster != null) {
                mainThreadPoster.enqueue(subscription, event);
            } else {
                // temporary: technically not correct as poster not decoupled from subscriber
                invokeSubscriber(subscription, event);
            }
            break;
        case BACKGROUND:
            if (isMainThread) {
                backgroundPoster.enqueue(subscription, event);
            } else {
                invokeSubscriber(subscription, event);
            }
            break;
        case ASYNC:
            asyncPoster.enqueue(subscription, event);
            break;
        default:
            throw new IllegalStateException("Unknown thread mode: " + subscription.subscriberMethod.threadMode);
    }
}

1.取出订阅方法的线程模式,之后根据线程模式来分别处理

2.内部直接采用反射调用,poster.enqueue内部也是采用反射调用结合handler处理

三、UnRegister

代码语言:txt
复制
public synchronized void unregister(Object subscriber) {
    List<Class<?>> subscribedTypes = typesBySubscriber.get(subscriber);
    if (subscribedTypes != null) {
        for (Class<?> eventType : subscribedTypes) {
            //对 subscriptionsByEventType 移除了该 subscriber 的所有订阅信息
            unsubscribeByEventType(subscriber, eventType);
        }
        //移除了注册对象和其对应的所有 Event 事件链表
        typesBySubscriber.remove(subscriber);
    } else {
        logger.log(Level.WARNING, "Subscriber to unregister was not registered before: " + subscriber.getClass());
    }
}
代码语言:txt
复制
/** Only updates subscriptionsByEventType, not typesBySubscriber! Caller must update typesBySubscriber. */
private void unsubscribeByEventType(Object subscriber, Class<?> eventType) {
    List<Subscription> subscriptions = subscriptionsByEventType.get(eventType);
    if (subscriptions != null) {
        int size = subscriptions.size();
        for (int i = 0; i < size; i++) {
            Subscription subscription = subscriptions.get(i);
            if (subscription.subscriber == subscriber) {
                subscription.active = false;
                subscriptions.remove(i);
                i--;
                size--;
            }
        }
    }
}

原创声明:本文系作者授权腾讯云开发者社区发表,未经许可,不得转载。

如有侵权,请联系 cloudcommunity@tencent.com 删除。

原创声明:本文系作者授权腾讯云开发者社区发表,未经许可,不得转载。

如有侵权,请联系 cloudcommunity@tencent.com 删除。

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • 一、Register
    • 一、findSubscriberMethods
      • 二、subscribe
      • 二、Post
        • 一、postSingleEvent
          • 一、lookupAllEventTypes
        • 二、postSingleEventForEventType
        • 三、UnRegister
        领券
        问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档