记一次AndroidEventBus的源码之旅:为何我的监听执行两次

前述

    虽然该库已经停止维护了,但是因为它注册监听的便捷,所以项目中还是用到了该库。想当初刚使用该库时候的方便,真真是乐不思蜀啊!功能就不赘述了,都停止维护还在研究这个就有点尬尴了。

问题描述

    post一个消息,Subscriber注解的方法总是执行两次。问题描述起来就很简单了,那是因为我并没有描述在网上寻找原因跟解决方案的艰辛,并且并未找到同样的问题,也隐藏了导致这个原因的重要细节描述,伪代码贴出来看大家是否知道是什么问题:

//监听:
@Subscriber(tag = "tag")
public void onXXX(Map obj) {
    //...
}

//发送消息
HashMap params = new HashMap<>();
params.put("aaa", "aaa");
params.put("bbb", "bbb");
EventBus.getDefault().post(params, "tag");
复制代码

解决方案

    解决方法就是吧@Subscriber注解的方法入参由Map改成HashMap就好了。这个问题按说把发送跟接收的数据类型统一就可以避免,但为什么会执行两次,如果压根就接收不到那也会比较早的发现问题了。为了能睡个安稳觉,也要把根本原因找到。(为了不耽误不想看我废话的朋友的时间,先把解决方法告诉大家,下面来看看原因)

源码追踪

    既然已经说了是废话了,那还是啰嗦一点。监听跟分发肯定是分两步走的,我们也分两个步骤走:

步骤一:注册

当通过 EventBus.register(Object subscriber)注册对象后,SubsciberMethodHunter类做如下操作:

  • 通过遍历subscriber里所有通过@Subscriber(tag="tag",mode=ThreadMode mode)注解的方法Method method
    • 获取到tagmethod的入参类型,封装成EventType eventType对象(以供后期消息分发)
    • 获取到mode,把methodeventTypemode封装成TargetMethod subscribeMethod,再将subscribersubscribeMethod封装成Subscription newSubscription(以供消息分发后调用method来执行方法)
    • eventTypekey,将newSubscription放进ListListvalue,用map维护期来(放进List,是因为一种类型(tag)的消息你可能在多个对象里注册哦)

源码片段(代码有删减变动,仅为方便学习)

    public class SubsciberMethodHunter {
        Map> mSubcriberMap;//
    
        /**
        * 查找订阅对象中的所有订阅函数,订阅函数的参数只能有一个.找到订阅函数之后构建Subscription存储到Map中
        *
         * @param subscriber 订阅对象
        */
        public void findSubcribeMethods(Object subscriber) {
            Class clazz = subscriber.getClass();
            // 查找类中符合要求的注册方法,直到Object类
            while (clazz != null && !isSystemCalss(clazz.getName())) {
                // 获取到注册对象的所有方法并遍历
                final Method[] allMethods = clazz.getDeclaredMethods();
                for (int i = 0; i < allMethods.length; i++) {
                    // 获取到subscriber中通过@Subscriber()注解的所有方法,及Subscriber对象(tag,mode)
                    Subscriber annotation = method.getAnnotation(Subscriber.class);
                    if (annotation != null) {
                        // 获取参数类型,封装EventType
                        Class[] paramType = method.getParameterTypes();
                        EventType eventType = new EventType(paramType, annotation.tag());
                        // 封装TargetMethod
                         TargetMethod subscribeMethod = new TargetMethod(method, eventType, annotation.mode());
                        // 封装Subscription
                        Subscription newSubscription = new Subscription(subscriber, subscribeMethod);
                        // 将Subscription放进List
                        CopyOnWriteArrayList subscriptionLists = mSubcriberMap.get(event);
                        subscriptionLists.add(newSubscription);
                        // 将事件类型key和订阅者信息存储到map中
                        mSubcriberMap.put(event, subscriptionLists);
                    }
                }
            }
           // 获取父类,以继续查找父类中符合要求的方法
           clazz = clazz.getSuperclass();
        }
    }
    
    public final class EventType {
            /**
         * 参数类型
         */
        Class paramClass;
        /**
         * 函数的tag
         */
        public String tag = DEFAULT_TAG;
    }
复制代码
步骤二:发送消息

当通过 EventBus.post(Object param, String tag)发送消息后,EventBus类做如下操作:

  • eventtag封装成EventType放进队列(这里就很快的能想到注册时为什么以EventTypekey了)
  • 通过EventDispatcher类执行队列,进行消息的分发:
    • (重点)获取传参paramClass类、父类、接口类分别跟tag封装的EventType的集合List eventTypes(此举好处:post()发送HashMap参数,以HashMapAbstractMapMap都可以收到消息)
    • 通过List eventTypes注册时维护的Map> mSubcriberMap里获取到Subscription集合
    • 遍历CopyOnWriteArrayList,从Subscription中获取到订阅对象、订阅方法对象、mode(接收线程),进行消息分发

源码片段(代码有删减变动,仅为方便学习)

public final class EventBus {
    //放进队列,执行分发
    public void post(Object event, String tag) {
        mLocalEvents.get().offer(new EventType(event.getClass(), tag));
        (EventDispatcher)mDispatcher.dispatchEvents(event);
    }
    
    private class EventDispatcher {
    
        MatchPolicy mMatchPolicy = new DefaultMatchPolicy();
        
        //执行队列
        void dispatchEvents(Object aEvent) {
            Queue eventsQueue = mLocalEvents.get();
            while (eventsQueue.size() > 0) {
                deliveryEvent(eventsQueue.poll(), aEvent);
            }
        }
        
        /**
         * 根据aEvent查找到所有匹配的集合,然后处理事件
         */
        private void deliveryEvent(EventType type, Object aEvent) {
            // 重点来了,获取传参`param`的`Class`类、父类、接口类分别跟`tag`封装的`EventType`的集合`List eventTypes`
            List eventTypes = mMatchPolicy.findMatchEventTypes(type, aEvent);
            // 迭代所有匹配的事件并且分发给订阅者
            for (EventType eventType : eventTypes) {
                //处理事件
                handleEvent(eventType, aEvent);
            }
        }
        
        //从注册时维护的mSubcriberMap里获取到Subscription集合,遍历执行消息
        private void handleEvent(EventType eventType, Object aEvent) {
            List subscriptions = mSubcriberMap.get(eventType);
            if (subscriptions == null) {
                return;
            }

            for (Subscription subscription : subscriptions) {
                // 处理事件
                eventHandler.handleEvent(subscription, aEvent);
            }
        }
    }
}
复制代码

重点:获取传参paramClass类、父类、接口类分别跟tag封装的EventType的集合List eventTypes

public class DefaultMatchPolicy implements MatchPolicy {
    @Override
    public List findMatchEventTypes(EventType type, Object aEvent) {
        Class eventClass = aEvent.getClass();
        List result = new LinkedList();
        while (eventClass != null) {
            //将class类封装成EventType添加进集合
            result.add(new EventType(eventClass, type.tag));
            //获取实现的接口类封装成EventType添加进集合
            addInterfaces(result, eventClass, type.tag);
            //获取父类,while()继续执行
            eventClass = eventClass.getSuperclass();
        }
        return result;
    }
    //获取实现的接口类封装成EventType添加进集合
    private void addInterfaces(List eventTypes, Class eventClass, String tag) {
        Class[] interfacesClasses = eventClass.getInterfaces();
        for (Class interfaceClass : interfacesClasses) {
            if (!eventTypes.contains(interfaceClass)) {
                eventTypes.add(new EventType(interfaceClass, tag));
                addInterfaces(eventTypes, interfaceClass, tag);
            }
        }
    }
}
复制代码

注意: 按说这没什么毛病啊,你监听我消息类型的父类或是接口类都能收到消息,是的,你可能忘了我遇到的问题是啥了,收到两次消息啊!断点里我看到findMatchEventTypes()返回的List里有两个Map类的EventType,而我@Subscriber监听的正是Map类型的参数。看下问题出在哪:

// HashMap 继承 AbstractMap,实现 Map
public class HashMap extends AbstractMap implements Map, Cloneable, Serializable {
}

// AbstractMap 实现 Map
public abstract class AbstractMap implements Map {
}
复制代码

可能是我对java的继承跟实现有理解的不够深入,这样做的好处在哪里?大神可以留言指点指点。

彻底的解决方案

  1. EventBus.setMatchPolicy(MatchPolicy policy);设置订阅函数匹配策略为StrictMatchPolicy,这样的话就是精确匹配了,发什么数据类型,必须以什么类型接收。
  2. DefaultMatchPolicy.findMatchEventTypes()里的List去重

最后

第一次写技术文章,水平比较菜,主要以学习为主,说的不对的地方还望指点,不喜勿喷哦!喷我我会尴尬的哦,尬尴的哦,尴尬,尬尴尴尬。。。

转载于:https://juejin.im/post/5d42ad82f265da039401e8e9

你可能感兴趣的:(记一次AndroidEventBus的源码之旅:为何我的监听执行两次)