AndroidEventBus源码解析

前言

用过EventBus的小伙伴们应该都知道EventBus有两个版本, 一个是GreenRobot的EventBus, 一个是何洪辉大神的AndroidEventBus.
初学者可能会犹豫使用哪个更好一点呢, 其实AndroidEventBus是EventBus的简化版.
EventBus可能性能更好一点, 功能更强大一点, 支持自定义EventBusBuilder, 支持编译时自动生成代码的导入, 从而实现快速查找订阅方法, 而不是通过反射查找.
但是AndroidEventBus使用起来更加简单, 例如使用EventBus的话, 发布一个String类型的事件, 那么所有事件类型是String的订阅方法都会受到这个事件, 要解决这个问题就需要自定义事件来区分. 而AndroidEventBus是通过事件类型和一个tag来区分不同的事件, 就不用担心事件类型重复的问题了, 使用起来更加方便.
我在上篇文章已经分析过EventBus3.0的源码了, 这篇文章主要来探讨一下AndroidEventBus的源码(何洪辉大神在好多地方都有注释, 看起来也轻松好多), 看看两者究竟有什么区别.

AndroidEventBus的使用

AndroidEventBus的使用可以参考何洪辉大神在ReadMe中的说明, 我就不再啰嗦.

AndroidEventBus源码解析

先看几个基本类:

  • 注解Subscriber
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.METHOD)
public @interface Subscriber {
    //事件的标识符
    String tag() default EventType.DEFAULT_TAG;

    //事件执行的线程,默认为主线程
    ThreadMode mode() default ThreadMode.MAIN;
}
  • ThreadMode事件发布的线程
public enum ThreadMode {
    //事件执行在UI线程
    MAIN,
    //事件执行在发布线程
    POST,
    //事件执行在一个子线程中
    ASYNC
}
  • 事件EventType, 包括参数类型和tag, 保证对象的唯一性
    //默认tag
    public static final String DEFAULT_TAG = "default_tag"
    // 参数类型
    Class<?> paramClass;
    //函数的tag
    public String tag = DEFAULT_TAG;
    public Object event ;
  • 订阅方法TargetMethod, 包含目标函数, 事件和线程模型
    //订阅者的目标函数
    public Method method;
    //事件类型
    public EventType eventType ;
    //处理事件的线程模式
    public ThreadMode threadMode;
  • 订阅关系Subscription, 包含订阅者对象和订阅方法
    //订阅者对象
    public Reference<Object> subscriber;
    //目标方法
    public Method targetMethod;
    //执行事件的线程模型
    public ThreadMode threadMode;
    // 事件类型
    public EventType eventType;

初始化

EventBus的初始化很简单, 就一个字符串来描述EventBus

public static EventBus getDefault() {
        if (sDefaultBus == null) {
            synchronized (EventBus.class) {
                if (sDefaultBus == null) {
                    sDefaultBus = new EventBus();
                }
            }
        }
        return sDefaultBus;
    }
public EventBus(String desc) {
        mDesc = desc;
    }

然后熟悉一下这几个属性, 后面会经常用到:

//保存所有事件类型及订阅关系   键: 事件类型,  值: 事件类型对应的订阅关系集合
private final Map<EventType, CopyOnWriteArrayList<Subscription>> mSubcriberMap = new ConcurrentHashMap<EventType, CopyOnWriteArrayList<Subscription>>();
//保存所有粘性事件的集合 
private List<EventType> mStickyEvents = Collections
            .synchronizedList(new LinkedList<EventType>());
//事件队列, 用ThreadLocal包裹保证每个线程都有唯一的队列, 互不影响
ThreadLocal<Queue<EventType>> mLocalEvents = new ThreadLocal<Queue<EventType>>() {
        protected java.util.Queue<EventType> initialValue() {
            return new ConcurrentLinkedQueue<EventType>();
        };
    };

然后就到重点: 注册流程啦!

注册流程

AndroidEventBus的注册方法有两个, 一个是普通事件的注册, 一个是粘性事件的注册. (EventBus3.0只有一个注册方法, 通过订阅方法注解中的sticky判断是否是粘性事件)

  1. 普通事件的注册
public void register(Object subscriber) {
        if (subscriber == null) {
            return;
        }

        synchronized (this) {
            //通过方法查找器查找该订阅者的订阅方法
            mMethodHunter.findSubcribeMethods(subscriber);
        }
    }
public void findSubcribeMethods(Object subscriber) {
        if (mSubcriberMap == null) {
            throw new NullPointerException("the mSubcriberMap is null. ");
        }
        Class<?> clazz = subscriber.getClass();
        // 循环查找该类或父类中符合要求的注册方法,直到系统类(以java或javax, android开头)
        while (clazz != null && !isSystemCalss(clazz.getName())) {
            //通过反射获取所有方法
            final Method[] allMethods = clazz.getDeclaredMethods();
            for (int i = 0; i < allMethods.length; i++) {
                Method method = allMethods[i];
                // 是否是有Subscriber
                Subscriber annotation = method.getAnnotation(Subscriber.class);
                if (annotation != null) {
                    // 获取方法的所有参数
                    Class<?>[] paramsTypeClass = method.getParameterTypes();
                    // 保证订阅方法只有一个参数
                    if (paramsTypeClass != null && paramsTypeClass.length == 1) {
                        Class<?> paramType = convertType(paramsTypeClass[0]);
                        //参数类型和tag组成一个事件
                        EventType eventType = new EventType(paramType, annotation.tag());
                        //该方法,事件, 线程模型组成一个订阅方法
                        TargetMethod subscribeMethod = new TargetMethod(method, eventType,
                                annotation.mode());
                        //订阅该事件及方法
                        subscibe(eventType, subscribeMethod, subscriber);
                    }
                }
            } 
            // 获取父类,以继续查找父类中符合要求的方法
            clazz = clazz.getSuperclass();
        }
    }
private void subscibe(EventType event, TargetMethod method, Object subscriber) {
        //根据事件取出所有订阅关系的集合
        CopyOnWriteArrayList<Subscription> subscriptionLists = mSubcriberMap.get(event);
        if (subscriptionLists == null) {
            subscriptionLists = new CopyOnWriteArrayList<Subscription>();
        }
        //把订阅者和订阅方法组成订阅关系
        Subscription newSubscription = new Subscription(subscriber, method);
        if (subscriptionLists.contains(newSubscription)) {
            return;
        }
        //加入到集合中
        subscriptionLists.add(newSubscription);
        // 将事件类型key和订阅者信息存储到map中
        mSubcriberMap.put(event, subscriptionLists);
    }

注册流程就结束了, 是不是比EventBus简单得多, 少了PendingPost,PendingPostQueue, FindState这些辅助对象, 少了使用EventBusAnnotationProcessor生成MyEventBusIndex来查找订阅方法, 也少了事件优先级等.
画个图来总结一下普通事件的注册流程:

register.png
  1. 粘性事件的注册
public void registerSticky(Object subscriber) {
        //和普通事件注册
        this.register(subscriber);
        // 注册完马上去处理sticky事件
        mDispatcher.dispatchStickyEvents(subscriber);
    }

mDispatcher是一个事件分发器EventDispatcher, 放到后面事件分发里一起讲吧.
注册完了, 再看下取消注册的代码:

  1. 取消注册
public void unregister(Object subscriber) {
        if (subscriber == null) {
            return;
        }
        synchronized (this) {
            //通过订阅方法查找器移除订阅者信息
            mMethodHunter.removeMethodsFromMap(subscriber);
        }
    }
public void removeMethodsFromMap(Object subscriber) {
        //获取mSubcriberMap中所有订阅关系的集合
        Iterator<CopyOnWriteArrayList<Subscription>> iterator = mSubcriberMap
                .values().iterator();
        while (iterator.hasNext()) {
            CopyOnWriteArrayList<Subscription> subscriptions = iterator.next();
            if (subscriptions != null) {
                 //保存找到订阅关系
                List<Subscription> foundSubscriptions = new
                        LinkedList<Subscription>();
                Iterator<Subscription> subIterator = subscriptions.iterator();
                //遍历每一个订阅关系
                while (subIterator.hasNext()) {
                    Subscription subscription = subIterator.next();
                    // 获取订阅者对象的引用
                    Object cacheObject = subscription.subscriber.get();
                    //如果该订阅方法中订阅者等于要移除的订阅者, 或者该订阅方法中订阅者为空, 则加入foundSubscriptions
                    if ( isObjectsEqual(cacheObject, subscriber )
                            || cacheObject == null ) {
                        foundSubscriptions.add(subscription);
                    }
                }

                // 移除该subscriber的相关的Subscription
                subscriptions.removeAll(foundSubscriptions);
            }

            // 如果针对某个Event的订阅者数量为空了,那么需要从map中清除
            if (subscriptions == null || subscriptions.size() == 0) {
                iterator.remove();
            }
        }
    }

事件分发器

分析事件分发流程之前, 先看下事件分发器, AndroidEventBus通过EventDispatcher来实现事件的分发, EventDispatcher里面有三大Handler来切换线程.

private class EventDispatcher {

        //主线程事件处理器, 将接收方法执行在UI线程
        EventHandler mUIThreadEventHandler = new UIThreadEventHandler();

        //默认线程处理器, 哪个线程执行的post,接收方法就执行在哪个线程
        EventHandler mPostThreadHandler = new DefaultEventHandler();

        //异步线程处理器, 异步线程中执行订阅方法
        EventHandler mAsyncEventHandler = new AsyncEventHandler();
}

可以看到三个handler都实现了EventHandler这个接口, 然后看下三大Handler的具体实现.

public interface EventHandler {
    /**
     * 处理事件
     * 
     * @param subscription 订阅对象
     * @param event 待处理的事件
     */
    void handleEvent(Subscription subscription, Object event);
}

1. 默认线程处理器DefaultEventHandler

public class DefaultEventHandler implements EventHandler {
    
    public void handleEvent(Subscription subscription, Object event) {
        //订阅关系及订阅者对象不能为空
        if (subscription == null  || subscription.subscriber.get() == null) {
            return;
        }
        try {
            // 直接在当前线程通过反射调用订阅者的订阅方法, 不用切换线程
            subscription.targetMethod.invoke(subscription.subscriber.get(), event);
        } catch (IllegalArgumentException e) {
            ...
        } 
}

2. 主线程处理器UIThreadEventHandler

public class UIThreadEventHandler implements EventHandler {

    //主线程的handler
    private Handler mUIHandler = new Handler(Looper.getMainLooper());
    //默认线程处理线程器
    DefaultEventHandler mEventHandler = new DefaultEventHandler();

    public void handleEvent(final Subscription subscription, final Object event) {
         //把当前订阅关系和事件Post到主线程中执行
        mUIHandler.post(new Runnable() {

            @Override
            public void run() {
               mEventHandler.handleEvent(subscription, event);
            }
        });
    }
}

3. 异步线程处理器AsyncEventHandler

public class AsyncEventHandler implements EventHandler {

    //事件分发线程
    DispatcherThread mDispatcherThread;
    //默认线程处理器
    EventHandler mEventHandler = new DefaultEventHandler();

    public AsyncEventHandler() {
        mDispatcherThread = new DispatcherThread(AsyncEventHandler.class.getSimpleName());
        mDispatcherThread.start();
    }

    public void handleEvent(final Subscription subscription, final Object event) {
        //把当前订阅关系和事件Post到异步线程中执行
        mDispatcherThread.post(new Runnable() {

            @Override
            public void run() {
                mEventHandler.handleEvent(subscription, event);
            }
        });
    }

     //继承了HandlerThread , HandlerThread本质就是一个Thread, 只不过内部自动建立了一个Looper
    class DispatcherThread extends HandlerThread {

        //关联了AsyncExecutor消息队列的Handler
        protected Handler mAsyncHandler;

        public DispatcherThread(String name) {
            super(name);
        }

        public void post(Runnable runnable) {
            if (mAsyncHandler == null) {
                throw new NullPointerException("mAsyncHandler == null, please call start() first.");
            }
            mAsyncHandler.post(runnable);
        }

        @Override
        public synchronized void start() {
            super.start();
           //获取Looper, 创建子线程的handler
            mAsyncHandler = new Handler(this.getLooper());
        }
    }
}

事件发布流程

事件的发布也分为普通事件的发布和粘性事件的发布.
1. 发布普通事件

public void post(Object event) {
        post(event, EventType.DEFAULT_TAG);
    }
public void post(Object event, String tag) {
        //事件参数和tag组成事件, 加入到mLocalEvents事件队列中
        mLocalEvents.get().offer(new EventType(event.getClass(), tag));
        //通过事件分发器mDispatcher分发该事件
        mDispatcher.dispatchEvents(event);
    }

再来看EventDispatcher中的具体方法

void dispatchEvents(Object aEvent) {
           //获取到队列中所有事件
            Queue<EventType> eventsQueue = mLocalEvents.get();
            while (eventsQueue.size() > 0) {
                //逐一从队列中取出事件并分发
                deliveryEvent(eventsQueue.poll(), aEvent);
            }
        }
private void deliveryEvent(EventType type, Object aEvent) {
            // 获取匹配的事件集合
            List<EventType> eventTypes = getMatchedEventTypes(type, aEvent);
            // 迭代所有匹配的事件, 单独处理
            for (EventType eventType : eventTypes) {
                handleEvent(eventType, aEvent);
            }
        }
MatchPolicy mMatchPolicy = new DefaultMatchPolicy();
private List<EventType> getMatchedEventTypes(EventType type, Object aEvent) {
            List<EventType> eventTypes = null;
            //缓存所有事件类型  键: 事件  值: 该事件相关联的所有事件
            // 如果有缓存则直接从缓存中取
            if (mCacheEventTypes.containsKey(type)) {
                eventTypes = mCacheEventTypes.get(type);
            } else {
                //缓存中没有就通过事件匹配策略获取, 然后放入缓存mCacheEventTypes
                eventTypes = mMatchPolicy.findMatchEventTypes(type, aEvent);
                mCacheEventTypes.put(type, eventTypes);
            }

            return eventTypes != null ? eventTypes : new ArrayList<EventType>();
        }

这里多了一个事件匹配策略MatchPolicy, 有两个实现, 一个是默认的匹配策略DefaultMatchPolicy, 一个是严格匹配策略StrictMatchPolicy.

public interface MatchPolicy {
    List<EventType> findMatchEventTypes(EventType type, Object aEvent);
}

先来看下默认匹配策略DefaultMatchPolicy:

public class DefaultMatchPolicy implements MatchPolicy {

    @Override
    public List<EventType> findMatchEventTypes(EventType type, Object aEvent) {
        Class<?> eventClass = aEvent.getClass();
        List<EventType> result = new LinkedList<EventType>();
        while (eventClass != null) {
            //将参数类型和tag组成事件加入事件集合中
            result.add(new EventType(eventClass, type.tag));
            //如果是接口, 也组成事件加入事件集合
            addInterfaces(result, eventClass, type.tag);
            //循环遍历对象的父类
            eventClass = eventClass.getSuperclass();
        }

        return result;
    }

    //遍历该事件实现的所有接口
    private void addInterfaces(List<EventType> eventTypes, Class<?> eventClass, String tag) {
        if (eventClass == null) {
            return;
        }
        //如果是接口, 获取所有实现的接口
        Class<?>[] interfacesClasses = eventClass.getInterfaces();
        for (Class<?> interfaceClass : interfacesClasses) {
            if (!eventTypes.contains(interfaceClass)) {
                //把参数的父类接口和tag组成事件加入事件集合
                eventTypes.add(new EventType(interfaceClass, tag));
                //循环遍历实现接口的父接口
                addInterfaces(eventTypes, interfaceClass, tag);
            }
        }
    }
}

恍然大悟, 原来这个默认匹配策略就是查找该事件及其所有父类或实现接口及父接口事件的, 再来看下严格匹配策略StrictMatchPolicy

public class StrictMatchPolicy implements MatchPolicy {

    @Override
    public List<EventType> findMatchEventTypes(EventType type, Object aEvent) {
        List<EventType> result = new LinkedList<EventType>();
        //只加入当前事件, 不管父类及接口事件
        result.add(type);
        return result;
    }
}

回到上面, 获取到所有匹配事件之后, 一一处理单个事件:

private void handleEvent(EventType eventType, Object aEvent) {
            //从订阅时保存的mSubcriberMap中获取事件对应的所有订阅关系
            List<Subscription> subscriptions = mSubcriberMap.get(eventType);
            if (subscriptions == null) {
                return;
            }

            for (Subscription subscription : subscriptions) {
                final ThreadMode mode = subscription.threadMode;
                //根据线程模型获取相对应的处理器
                EventHandler eventHandler = getEventHandler(mode);
                // 交给对应的线程处理器来处理事件
                eventHandler.handleEvent(subscription, aEvent);
            }
        }
private EventHandler getEventHandler(ThreadMode mode) {
            //ASYNC返回异步线程处理器
            if (mode == ThreadMode.ASYNC) {
                return mAsyncEventHandler;
            }
            //POST返回默认线程处理器
            if (mode == ThreadMode.POST) {
                return mPostThreadHandler;
            }
            //不指定ThreadMode, 默认为MAIN, 返回主线程处理器
            return mUIThreadEventHandler;
        }

就是根据线程模型把订阅关系分发到相对应的处理器中执行, 三大Handler的处理逻辑上面已经分析过.
画个图总结一下普通事件的注册流程就是:

post.png

2.发布粘性事件

public void postSticky(Object event) {
        postSticky(event, EventType.DEFAULT_TAG);
    }
public void postSticky(Object event, String tag) {
        EventType eventType = new EventType(event.getClass(), tag);
        eventType.event = event;
        mStickyEvents.add(eventType);
    }

发布粘性事件只是把该事件存入mStickyEvents, 注册粘性事件的时候才会从mStickyEvents中取出粘性事件一一处理. 这就决定了AndroidEventBus的粘性事件必须先postSticky, 然后registerSticky才能生效.
而EventBus对粘性事件的处理是, 注册的时候通过注解中的sticky判断是否是粘性事件, 是的话就从粘性事件集合stickyEvents中取出并处理(先分发再注册的场景), stickyEvents中没有的话暂不处理(先注册再分发的场景). 分发粘性事件的时候, 先加入粘性事件集合stickyEvents, 再分发事件. 这就保证了不管是先注册再分发还是先分发再注册都可以有效处理事件.
这也是AndroidEventBus和EventBus的一点区别, 使用的时候不要搞混.

接着上面漏掉的注册粘性事件时通过事件分发器mDispatcher.dispatchStickyEvents(subscriber)处理的流程:

void dispatchStickyEvents(Object subscriber) {
            //遍历所有粘性事件集合
            for (EventType eventType : mStickyEvents) {
                //处理单个粘性事件
                handleStickyEvent(eventType, subscriber);
            }
        }
private void handleStickyEvent(EventType eventType, Object subscriber) {
            //getMatchedEventTypes获取该事件类型的所有父类及实现接口及父接口
            List<EventType> eventTypes = getMatchedEventTypes(eventType, eventType.event);
            // 事件
            Object event = eventType.event;
            for (EventType foundEventType : eventTypes) {
                //通过事件, 获取缓存中的订阅关系集合
                final List<Subscription> subscriptions = mSubcriberMap.get(foundEventType);
                if (subscriptions == null) {
                    continue;
                }
                for (Subscription subItem : subscriptions) {
                    final ThreadMode mode = subItem.threadMode;
                    //根据threadMode获取对应的线程处理器
                    EventHandler eventHandler = getEventHandler(mode);
                    // 如果传进来的订阅者为空,那么该sticky事件分发给所有订阅者.否则只分发给该订阅者
                    if (isTarget(subItem, subscriber)
                            && (subItem.eventType.equals(foundEventType)
                            || subItem.eventType.paramClass
                            .isAssignableFrom(foundEventType.paramClass))) {
                        // 处理事件
                        eventHandler.handleEvent(subItem, event);
                    }
                }
            }
        }

用一个图总结一下粘性事件的注册流程:

register_sticky.png

3. 删除粘性事件

public void removeStickyEvent(Class<?> eventClass) {
        removeStickyEvent(eventClass, EventType.DEFAULT_TAG);
    }
public void removeStickyEvent(Class<?> eventClass, String tag) {
        //遍历所有粘性事件集合
        Iterator<EventType> iterator = mStickyEvents.iterator();
        while (iterator.hasNext()) {
            EventType eventType = iterator.next();
            if (eventType.paramClass.equals(eventClass)
                    && eventType.tag.equals(tag)) {
                iterator.remove();
            }
        }
    }

很简单, 就是把该事件类型和tag组成的事件从粘性事件集合中移除.
OK, AndroidEventBus的源码看完了, 对比两个框架就能看出各自的利弊, 相信你也不会再把它俩搞混,不知道使用哪个了.

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 158,117评论 4 360
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 66,963评论 1 290
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 107,897评论 0 240
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 43,805评论 0 203
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 52,208评论 3 286
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 40,535评论 1 216
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 31,797评论 2 311
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 30,493评论 0 197
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 34,215评论 1 241
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 30,477评论 2 244
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 31,988评论 1 258
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 28,325评论 2 252
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 32,971评论 3 235
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 26,055评论 0 8
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 26,807评论 0 194
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 35,544评论 2 271
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 35,455评论 2 266

推荐阅读更多精彩内容