EventBus源码分析-register和unregister方法

9 篇文章 0 订阅
2 篇文章 0 订阅

参考链接:https://blog.csdn.net/qq_38859786/article/details/80285705

https://www.jianshu.com/p/e1d52c7f2581

1.register方法分析:

先上源码

public void register(Object subscriber) {
    Class<?> subscriberClass = subscriber.getClass();
    //找到订阅者的方法.找出传进来的订阅者的所有订阅方法,然后遍历订阅者的方法.
  List<SubscriberMethod> subscriberMethods = subscriberMethodFinder.findSubscriberMethods(subscriberClass);
    synchronized (this) {
        for (SubscriberMethod subscriberMethod : subscriberMethods) {
            //订阅者的注册
             subscribe(subscriber, subscriberMethod);
        }
    }
}
List<SubscriberMethod> findSubscriberMethods(Class<?> subscriberClass) {
        List<SubscriberMethod> subscriberMethods = METHOD_CACHE.get(subscriberClass); //首先从METHOD_CACHE中读取缓存下来的订阅方法列表
        if (subscriberMethods != null) {
            return subscriberMethods;
        }
        // ignoreGeneratedIndex属性表示是否忽略注解器生成的MyEventBusIndex。如何生成MyEventBusIndex类以及他的使用,可以本文中apt部分的详细说明。ignoreGeneratedIndex的默认值为false,可以通过EventBusBuilder来设置它的值
        if (ignoreGeneratedIndex) {
            // 利用反射来获取订阅类中所有订阅方法信息
            subscriberMethods = findUsingReflection(subscriberClass);
        } else {
            // 从注解器生成的MyEventBusIndex类中获得订阅类的订阅方法信息
            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;
        }
    }

这一步就是利用反射或者索引找到当前的所有订阅方法,组建成SubscriberMethod列表返回,并且此处会对注册类的所有SubscriberMethod做一次缓存,以加快下一次注册的速度

接下来回到register方法里,对获取到的SubscriberMethod列表进行for循环订阅subscribe

在这里插入图片描述

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 {
            if (subscriptions.contains(newSubscription)) {
                throw new EventBusException("Subscriber " + subscriber.getClass() + " already registered to event "
                        + eventType);
            }
        }
        
        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;
            }
        }
          
		// 获取此订阅者实例订阅的所有事件类的列表
        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.用当前订阅对象和当前订阅方法组合成订阅(Subscription)对象

    2.EventBus里有一个以EventType(即事件类,Class类型)为key,CopyOnWriteArrayList<Subscription>为值的HashMap subscriptionsByEventType,这样就可以通过一个类型找到它的所有订阅(Subscription)

     3.从subscriptionsByEventType里取出List<Subscription>集合,没有则新建一个CopyOnWriteArrayList并以当前SubscriberMethod的订阅类型(EventType)为键put到subscriptionsByEventType中

    4.第三步 取出来/新建的 CopyOnWriteArrayList<Subscription>集合判断是否已经有第1步建立的订阅对象,如果有的话则抛出异常报错,没有则将其 按照当前订阅方法(SubscriberMethod)的优先级add到集合中

    5.从Eventbus里的typesBySubscriber(HashMap,以注册者对象为key,以注册者订阅类型集合为键)获取/新建一个 当前注册对象的 所有 订阅类型 的集合(List<Class<?>>),并将当前订阅方法的事件类型(EventType)添加到集合中。后面unregister时就会很方便

    6.粘性事件处理,如果当前订阅方法的类型是粘性事件 (subscriberMethod.sticky),则进入粘性事件分发流程,EventBus有一个以事件类型(EventType)为key,发送了的粘性事件对象为键的ConcurrentHashMap<Class<?>, Object> stickyEvents,粘性事件处理时会先从stickyEvents里查找是否有当前订阅方法 事件类型(EventType)的粘性事件,如果有则通过 不同的threadMode(标识在哪个线程执行,有POSTING,MAIN,BACKGROUND,ASYNC 四种模式)分发到不同的线程并用当前注册对象反射调用当前的订阅方法

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

可见粘性是在register时调用的,如果此时Activity或者Fragment的view没有准备好则会出现奔溃

2.unregister方法分析

先上源码

public synchronized void unregister(Object subscriber) {
		// 获取订阅对象的所有订阅事件类列表
     	//typesBySubscriber上面5.提到的map
        List<Class<?>> subscribedTypes = typesBySubscriber.get(subscriber); 
        if (subscribedTypes != null) {
            for (Class<?> eventType : subscribedTypes) {
                unsubscribeByEventType(subscriber, eventType); // 将订阅者的订阅信息移除
            }
            typesBySubscriber.remove(subscriber); // 将订阅者从列表中移除
        } else {
            Log.w(TAG, "Subscriber to unregister was not registered before: " + subscriber.getClass());
        }
    }
private void unsubscribeByEventType(Object subscriber, Class<?> eventType) {
		// 获取事件类的所有订阅信息列表,将订阅信息从订阅信息集合中移除,同时将订阅信息中的active属性置为FALSE
        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; // 将订阅信息激活状态置为FALSE
                    subscriptions.remove(i); // 将订阅信息从集合中移除
                    i--;
                    size--;
                }
            }
        }
    }

找到当前注册对象的所有订阅事件类型,然后一个个的调用unsubscribeByEventType方法,将当前注册对象从其订阅类型的订阅者集合表移除,即找到 当前订阅者的 所有 订阅类型(EventType),通过 订阅类型 找到该订阅类型 的 订阅集合List<Subscription> ,将当前注册者从集合中移除

最后从typesBySubscriber(<订阅者,订阅类型>map)里将当前订阅者移除

总结:

  • register
    1. 通过注解找到当前类所有订阅方法(被@Subscribe注解的方法),并对当前注册类的所有订阅方法做缓存
    2. for循环注册当前注册者的所有订阅方法,每个方法的订阅如下
    3. 用当前订阅对象和订阅方法组合成一个订阅信息对象,然后将其依据优先级加入到订阅者列表中。并将列表put到以订阅类型为key的map中方便后面查找。
    4. 将当前订阅事件类型添加到当前注册者的订阅类型集合,该集合保存在一个以订阅者为key的map中,方便后边注销(unregistered)
    5. 粘性事件处理。如果当前订阅方法是一个粘性事件,则进入粘性事件分发流程(在粘性事件列表中找到有没有人发布过该粘性事件,有的话就调用相关订阅方法)
    6. 注:订阅方法的调用就是通过放射调用了注册的方法
  • unregister
    1. 用缓存了所有订阅者订阅信息的map(以订阅者为key,以该订阅者的所有订阅信息为集合为value)总找到当前订阅者的所有订阅类型集合,对所有的订阅形象进行注销
    2. 即根据事件类型找到其订阅者列表,将该订阅者从列表中移除
    3. 订阅者订阅信息map中移除该订阅者
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值