EventBus源码解析

EventBus的基本用法

注册事件
EventBus.getDefault().register(this);
解除注册
EventBus.getDefault().unregister(this);
发送事件
EventBus.getDefault().post(event);
处理事件
@Subscribe(threadMode = ThreadMode.MAIN)
public void XXX(Object event) {
    ...
}

注解@Subscribe

@Documented
@Retention(RetentionPolicy.RUNTIME)
@Target({ElementType.METHOD})
public @interface Subscribe {
    ThreadMode threadMode() default ThreadMode.POSTING;

    /**
     * If true, delivers the most recent sticky event (posted with
     * {@link EventBus#postSticky(Object)}) to this subscriber (if event available).
     */
    boolean sticky() default false;

    /** Subscriber priority to influence the order of event delivery.
     * Within the same delivery thread ({@link ThreadMode}), higher priority subscribers will receive events before
     * others with a lower priority. The default priority is 0. Note: the priority does *NOT* affect the order of
     * delivery among subscribers with different {@link ThreadMode}s! */
    int priority() default 0;
}

这是一个自定义的运行时注解,有三个属性,threadMode、sticky、priority。
threadMode表示处理事件所在的线程,有POSTING、MAIN、BACKGROUND和ASYNC四种线程模型,默认为POSTING。
sticky是否是粘性事件,默认为false。
priority为优先级,默认值为0,值越大优先级越高。

EventBus的getDefault()

EventBus.getDefault()采用单例模式,创建一个EventBus对象,初始化了subscriptionsByEventType和typesBySubscriber两个HashMap,用以存储订阅者、订阅方法和订阅事件的相关信息。

//这是一个单例模式
public static EventBus getDefault() {
    if (defaultInstance == null) {
        synchronized (EventBus.class) {
            if (defaultInstance == null) {
                //创建了一个EventBus对象
                defaultInstance = new EventBus();
            }
        }
    }
    return defaultInstance;
}

public EventBus() {
    this(DEFAULT_BUILDER);
}

EventBus(EventBusBuilder builder) {
    //一个HashMap,以事件类型eventType为key,以存储了订阅者和订阅方法的集合CopyOnWriteArrayList<Subscription>为value
    subscriptionsByEventType = new HashMap<>();
    //一个HashMap,以订阅者subscriber为key,以订阅者所订阅的事件类型eventType的集合List<Class<?>>为value
    typesBySubscriber = new HashMap<>();
    //粘性事件相关的集合,这里不对粘性事件做分析,有兴趣的自行学习
    stickyEvents = new ConcurrentHashMap<>();
    //主线线程事件发送者
    mainThreadPoster = new HandlerPoster(this, Looper.getMainLooper(), 10);
    //后台线程事件发送者
    backgroundPoster = new BackgroundPoster(this);
    //异步线程事件发送者
    asyncPoster = new AsyncPoster(this);
        ...
}

注册register

public void register(Object subscriber) {
    Class<?> subscriberClass = subscriber.getClass();
    //获取订阅者的订阅方法的集合,后面有对findSubscriberMethods的描述
    List<SubscriberMethod> subscriberMethods = subscriberMethodFinder.findSubscriberMethods(subscriberClass);
    synchronized (this) {
        for (SubscriberMethod subscriberMethod : subscriberMethods) {
            subscribe(subscriber, subscriberMethod);
        }
    }
}

// Must be called in synchronized block
private void subscribe(Object subscriber, SubscriberMethod subscriberMethod) {
    Class<?> eventType = subscriberMethod.eventType;
    //Subscription封装了订阅者subscriber和订阅方法subscriberMethod
    Subscription newSubscription = new Subscription(subscriber, subscriberMethod);
    CopyOnWriteArrayList<Subscription> subscriptions = subscriptionsByEventType.get(eventType);
    //subscriptions若为空,先初始化,subscriptions已包含newSubscription,说明已被注册过,抛异常
    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++) {
        //按优先级把newSubscription添加到subscriptions,即添加到subscriptionsByEventType
        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);
    }
    //把eventType添加到subscribedEvents,即添加到typesBySubscriber
    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);
        }
    }
}

从上面代码可以看出,注册其实就是把当前订阅者相关的所有newSubscription存入subscriptionsByEventType,把当前订阅者订阅的所有eventType存入typesBySubscriber。

因为newSubscription包含了订阅者subscriber和订阅方法subscriberMethod,subscriptionsByEventType的key为事件类型eventType,
所以在subscriptionsByEventType中可以根据事件类型eventType获取所有的类型为eventType的订阅方法subscriberMethod。因为typesBySubscriber的key为订阅者subscriber,所以在typesBySubscriber中可以根据订阅者subscriber获取订阅者订阅的所有的事件类型eventType。

subscriberMethodFinder.findSubscriberMethods(subscriberClass)

方法findSubscriberMethods是为了获取订阅者的订阅方法的集合List<SubscriberMethod>,查看findSubscriberMethods这个方法的源码可以发现如果不使用索引,最终会调用findUsingReflectionInSingleClass方法,而默认是不使用索引的,对索引感兴趣的请自行学习,下面是findUsingReflectionInSingleClass方法的源码:

private void findUsingReflectionInSingleClass(FindState findState) {
    Method[] methods;
    try {
        // This is faster than getMethods, especially when subscribers are fat classes like Activities
        methods = findState.clazz.getDeclaredMethods();
    } catch (Throwable th) {
        //获取订阅者的所有方法
        // Workaround for java.lang.NoClassDefFoundError, see https://github.com/greenrobot/EventBus/issues/149
        methods = findState.clazz.getMethods();
        findState.skipSuperClasses = true;
    }
    for (Method method : methods) {
        int modifiers = method.getModifiers();
        //方法名前有public,没有abstract、static
        if ((modifiers & Modifier.PUBLIC) != 0 && (modifiers & MODIFIERS_IGNORE) == 0) {
            Class<?>[] parameterTypes = method.getParameterTypes();
            //方法只有一个参数
            if (parameterTypes.length == 1) {
                //方法的@Subscribe注解
                Subscribe subscribeAnnotation = method.getAnnotation(Subscribe.class);
                if (subscribeAnnotation != null) {
                    Class<?> eventType = parameterTypes[0];
                    if (findState.checkAdd(method, eventType)) {
                        //获取注解中的threadMode
                        ThreadMode threadMode = subscribeAnnotation.threadMode();
                        //findState.subscriberMethods是一个List<SubscriberMethod>集合,创建订阅方法SubscriberMethod添加到集合中
                        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");
        }
    }
}

从上面源码中可以看出,集合findState.subscriberMethods就是我们要获取的集合,最后通过getMethodsAndRelease(FindState findState)从FindState中获取这个集合

解除注册unregister

/** Unregisters the given subscriber from all event classes. */
public synchronized void unregister(Object subscriber) {
    //根据订阅者subscriber获取订阅者订阅的所有的事件类型
    List<Class<?>> subscribedTypes = typesBySubscriber.get(subscriber);
    if (subscribedTypes != null) {
        for (Class<?> eventType : subscribedTypes) {
            //从subscriptionsByEventType中移除订阅者对应的subscription
            unsubscribeByEventType(subscriber, eventType);
        }
        //移除订阅者订阅的所有的事件类型
        typesBySubscriber.remove(subscriber);
    } else {
        Log.w(TAG, "Subscriber to unregister was not registered before: " + subscriber.getClass());
    }
}

/*
*从subscriptionsByEventType中移除订阅者对应的subscription,subscription包含subscriberMethod,
*所以其实是从subscriptionsByEventType中移除订阅者对应的处理eventType类型事件的方法subscriberMethod
*/
/** Only updates subscriptionsByEventType, not typesBySubscriber! Caller must update typesBySubscriber. */
private void unsubscribeByEventType(Object subscriber, Class<?> eventType) {
    //获取所有的类型为eventType的Subscription的集合
    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);
            //判断是否是当前订阅者subscriber
            if (subscription.subscriber == subscriber) {
                subscription.active = false;
                subscriptions.remove(i);
                i--;
                size--;
            }
        }
    }
}

从上面代码可以看出,解除注册其实就是从typesBySubscriber中移除订阅者订阅的所有的事件类型eventType,从subscriptionsByEventType中移除所有当前订阅者对应的处理eventType类型事件的方法subscriberMethod。

发送事件post

post方法最终调用的是postToSubscription方法,源码如下:

private void postToSubscription(Subscription subscription, Object event, boolean isMainThread) {
    switch (subscription.subscriberMethod.threadMode) {
        case POSTING:
            invokeSubscriber(subscription, event);
            break;
        case MAIN:
            if (isMainThread) {
                invokeSubscriber(subscription, event);
            } else {
                mainThreadPoster.enqueue(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);
    }
}

从上面可以看出发送者和处理函数在相同线程直接调用invokeSubscriber,不相同时,分别调用mainThreadPoster.enqueue、backgroundPoster.enqueue和asyncPoster.enqueue。其中mainThreadPoster.enqueue是通过Handler将消息发送到主线程最终再调用invokeSubscriber,而backgroundPoster.enqueue和asyncPoster.enqueue都是通过调用线程池的线程的run方法最终调用invokeSubscriber,所以postToSubscription方法最终调的都是invokeSubscriber方法,只是所在线程不同罢了。

从invokeSubscriber的源码可以看出,它通过反射调用处理事件的方法,方法的参数是event。

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

最后

EventBus维护了subscriptionsByEventType和typesBySubscriber两个HashMap,其中typesBySubscriber的key为订阅者subscriber,value为订阅者订阅的所有的事件类型eventType的集合,便于注册和解除注册时添加移除当前订阅者订阅的事件类型。subscriptionsByEventType的key为eventType,value为由订阅者和处理eventType类型事件的方法subscriberMethod组成的subscription对象的集合,便于发送事件时通过事件类型eventType获取处理对应事件的方法,最后通过反射机制调用该方法。


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

推荐阅读更多精彩内容