EventBus 原理解析

EventBus 是一款在 Android 開發(fā)中使用的發(fā)布/訂閱事件總線框架缔俄,基于觀察者模式弛秋,將事件的接收者和發(fā)送者分開,簡(jiǎn)化了組件之間的通信俐载,使用簡(jiǎn)單蟹略、效率高、體積卸粲丁挖炬!下邊是官方的 EventBus 原理圖:

EventBus 的用法可以參考官網(wǎng),這里不做過(guò)多的說(shuō)明状婶。本文主要是從 EventBus 使用的方式入手意敛,來(lái)分析 EventBus 背后的實(shí)現(xiàn)原理,以下內(nèi)容基于eventbus:3.1.1版本太抓,主要包括如下幾個(gè)方面的內(nèi)容:

  • Subscribe注解
  • 注冊(cè)事件訂閱方法
  • 取消注冊(cè)
  • 發(fā)送事件
  • 事件處理
  • 粘性事件
  • Subscriber Index
  • 核心流程梳理

一空闲、Subscribe注解

EventBus3.0 開始用Subscribe注解配置事件訂閱方法,不再使用方法名了走敌,例如:

@Subscribe
public void handleEvent(String event) {
    // do something
}

其中事件類型可以是 Java 中已有的類型或者我們自定義的類型碴倾。
具體看下Subscribe注解的實(shí)現(xiàn):

@Documented
@Retention(RetentionPolicy.RUNTIME)
@Target({ElementType.METHOD})
public @interface Subscribe {
    // 指定事件訂閱方法的線程模式,即在那個(gè)線程執(zhí)行事件訂閱方法處理事件掉丽,默認(rèn)為POSTING
    ThreadMode threadMode() default ThreadMode.POSTING;
    // 是否支持粘性事件跌榔,默認(rèn)為false
    boolean sticky() default false;
    // 指定事件訂閱方法的優(yōu)先級(jí),默認(rèn)為0捶障,如果多個(gè)事件訂閱方法可以接收相同事件的僧须,則優(yōu)先級(jí)高的先接收到事件
    int priority() default 0;
}

所以在使用Subscribe注解時(shí)可以根據(jù)需求指定threadModesticky项炼、priority三個(gè)屬性担平。

其中threadMode屬性有如下幾個(gè)可選值:

  • ThreadMode.POSTING示绊,默認(rèn)的線程模式,在那個(gè)線程發(fā)送事件就在對(duì)應(yīng)線程處理事件暂论,避免了線程切換面褐,效率高。
  • ThreadMode.MAIN取胎,如在主線程(UI線程)發(fā)送事件展哭,則直接在主線程處理事件;如果在子線程發(fā)送事件闻蛀,則先將事件入隊(duì)列匪傍,然后通過(guò) Handler 切換到主線程,依次處理事件觉痛。
  • ThreadMode.MAIN_ORDERED役衡,無(wú)論在那個(gè)線程發(fā)送事件,都先將事件入隊(duì)列秧饮,然后通過(guò) Handler 切換到主線程映挂,依次處理事件。
  • ThreadMode.BACKGROUND盗尸,如果在主線程發(fā)送事件柑船,則先將事件入隊(duì)列,然后通過(guò)線程池依次處理事件泼各;如果在子線程發(fā)送事件鞍时,則直接在發(fā)送事件的線程處理事件。
  • ThreadMode.ASYNC扣蜻,無(wú)論在那個(gè)線程發(fā)送事件逆巍,都將事件入隊(duì)列,然后通過(guò)線程池處理莽使。

二锐极、注冊(cè)事件訂閱方法

注冊(cè)事件的方式如下:

EventBus.getDefault().register(this);

其中getDefault()是一個(gè)單例方法,保證當(dāng)前只有一個(gè)EventBus實(shí)例:

public static EventBus getDefault() {
        if (defaultInstance == null) {
            synchronized (EventBus.class) {
                if (defaultInstance == null) {
                    defaultInstance = new EventBus();
                }
            }
        }
        return defaultInstance;
    }

繼續(xù)看new EventBus()做了些什么:

public EventBus() {
        this(DEFAULT_BUILDER);
    }

在這里又調(diào)用了EventBus的另一個(gè)構(gòu)造函數(shù)來(lái)完成它相關(guān)屬性的初始化:

EventBus(EventBusBuilder builder) {
        logger = builder.getLogger();
        subscriptionsByEventType = new HashMap<>();
        typesBySubscriber = new HashMap<>();
        stickyEvents = new ConcurrentHashMap<>();
        mainThreadSupport = builder.getMainThreadSupport();
        mainThreadPoster = mainThreadSupport != null ? mainThreadSupport.createPoster(this) : null;
        backgroundPoster = new BackgroundPoster(this);
        asyncPoster = new AsyncPoster(this);
        indexCount = builder.subscriberInfoIndexes != null ? builder.subscriberInfoIndexes.size() : 0;
        subscriberMethodFinder = new SubscriberMethodFinder(builder.subscriberInfoIndexes,
                builder.strictMethodVerification, builder.ignoreGeneratedIndex);
        logSubscriberExceptions = builder.logSubscriberExceptions;
        logNoSubscriberMessages = builder.logNoSubscriberMessages;
        sendSubscriberExceptionEvent = builder.sendSubscriberExceptionEvent;
        sendNoSubscriberEvent = builder.sendNoSubscriberEvent;
        throwSubscriberException = builder.throwSubscriberException;
        eventInheritance = builder.eventInheritance;
        executorService = builder.executorService;
    }

DEFAULT_BUILDER就是一個(gè)默認(rèn)的EventBusBuilder

private static final EventBusBuilder DEFAULT_BUILDER = new EventBusBuilder();

如果有需要的話芳肌,我們也可以通過(guò)配置EventBusBuilder來(lái)更改EventBus的屬性灵再,例如用如下方式注冊(cè)事件:

EventBus.builder()
        .eventInheritance(false)
        .logSubscriberExceptions(false)
        .build()
        .register(this);

有了EventBus的實(shí)例就可以進(jìn)行注冊(cè)了:

public void register(Object subscriber) {
        // 得到當(dāng)前要注冊(cè)類的Class對(duì)象
        Class<?> subscriberClass = subscriber.getClass();
        // 根據(jù)Class查找當(dāng)前類中訂閱了事件的方法集合,即使用了Subscribe注解亿笤、有public修飾符翎迁、一個(gè)參數(shù)的方法
        // SubscriberMethod類主要封裝了符合條件方法的相關(guān)信息:
        // Method對(duì)象、線程模式净薛、事件類型汪榔、優(yōu)先級(jí)、是否是粘性事等
        List<SubscriberMethod> subscriberMethods = subscriberMethodFinder.findSubscriberMethods(subscriberClass);
        synchronized (this) {
            // 循環(huán)遍歷訂閱了事件的方法集合肃拜,以完成注冊(cè)
            for (SubscriberMethod subscriberMethod : subscriberMethods) {
                subscribe(subscriber, subscriberMethod);
            }
        }
    }

可以看到register()方法主要分為查找和注冊(cè)兩部分痴腌,首先來(lái)看查找的過(guò)程雌团,從findSubscriberMethods()開始:

List<SubscriberMethod> findSubscriberMethods(Class<?> subscriberClass) {
        // METHOD_CACHE是一個(gè)ConcurrentHashMap,直接保存了subscriberClass和對(duì)應(yīng)SubscriberMethod的集合士聪,以提高注冊(cè)效率辱姨,賦值重復(fù)查找。
        List<SubscriberMethod> subscriberMethods = METHOD_CACHE.get(subscriberClass);
        if (subscriberMethods != null) {
            return subscriberMethods;
        }
        // 由于使用了默認(rèn)的EventBusBuilder戚嗅,則ignoreGeneratedIndex屬性默認(rèn)為false,即是否忽略注解生成器
        if (ignoreGeneratedIndex) {
            subscriberMethods = findUsingReflection(subscriberClass);
        } else {
            subscriberMethods = findUsingInfo(subscriberClass);
        }
        // 如果對(duì)應(yīng)類中沒(méi)有符合條件的方法枢舶,則拋出異常
        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;
        }
    }

findSubscriberMethods()流程很清晰懦胞,即先從緩存中查找,如果找到則直接返回凉泄,否則去做下一步的查找過(guò)程躏尉,然后緩存查找到的集合,根據(jù)上邊的注釋可知findUsingInfo()方法會(huì)被調(diào)用:

private List<SubscriberMethod> findUsingInfo(Class<?> subscriberClass) {
        FindState findState = prepareFindState();
        findState.initForSubscriber(subscriberClass);
        // 初始狀態(tài)下findState.clazz就是subscriberClass
        while (findState.clazz != null) {
            findState.subscriberInfo = getSubscriberInfo(findState);
            // 條件不成立
            if (findState.subscriberInfo != null) {
                SubscriberMethod[] array = findState.subscriberInfo.getSubscriberMethods();
                for (SubscriberMethod subscriberMethod : array) {
                    if (findState.checkAdd(subscriberMethod.method, subscriberMethod.eventType)) {
                        findState.subscriberMethods.add(subscriberMethod);
                    }
                }
            } else {
                // 通過(guò)反射查找訂閱事件的方法
                findUsingReflectionInSingleClass(findState);
            }
            // 修改findState.clazz為subscriberClass的父類Class后众,即需要遍歷父類
            findState.moveToSuperclass();
        }
        // 查找到的方法保存在了FindState實(shí)例的subscriberMethods集合中胀糜。
        // 使用subscriberMethods構(gòu)建一個(gè)新的List<SubscriberMethod>
        // 釋放掉findState
        return getMethodsAndRelease(findState);
    }

findUsingInfo()方法會(huì)在當(dāng)前要注冊(cè)的類以及其父類中查找訂閱事件的方法,這里出現(xiàn)了一個(gè)FindState類蒂誉,它是SubscriberMethodFinder的內(nèi)部類教藻,用來(lái)輔助查找訂閱事件的方法,具體的查找過(guò)程在findUsingReflectionInSingleClass()方法右锨,它主要通過(guò)反射查找訂閱事件的方法:

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;
        }
        // 循環(huán)遍歷當(dāng)前類的方法,篩選出符合條件的
        for (Method method : methods) {
            // 獲得方法的修飾符
            int modifiers = method.getModifiers();
            // 如果是public類型,但非abstract素征、static等
            if ((modifiers & Modifier.PUBLIC) != 0 && (modifiers & MODIFIERS_IGNORE) == 0) {
                // 獲得當(dāng)前方法所有參數(shù)的類型
                Class<?>[] parameterTypes = method.getParameterTypes();
                // 如果當(dāng)前方法只有一個(gè)參數(shù)
                if (parameterTypes.length == 1) {
                    Subscribe subscribeAnnotation = method.getAnnotation(Subscribe.class);
                    // 如果當(dāng)前方法使用了Subscribe注解
                    if (subscribeAnnotation != null) {
                        // 得到該參數(shù)的類型
                        Class<?> eventType = parameterTypes[0];
                        // checkAdd()方法用來(lái)判斷FindState的anyMethodByEventType map是否已經(jīng)添加過(guò)以當(dāng)前eventType為key的鍵值對(duì)急凰,沒(méi)添加過(guò)則返回true
                        if (findState.checkAdd(method, eventType)) {
                             // 得到Subscribe注解的threadMode屬性值,即線程模式
                            ThreadMode threadMode = subscribeAnnotation.threadMode();
                            // 創(chuàng)建一個(gè)SubscriberMethod對(duì)象蹂窖,并添加到subscriberMethods集合
                            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");
            }
        }
    }

到此register()方法中findSubscriberMethods()流程就分析完了轧抗,我們已經(jīng)找到了當(dāng)前注冊(cè)類及其父類中訂閱事件的方法的集合。接下來(lái)分析具體的注冊(cè)流程瞬测,即register()中的subscribe()方法:

private void subscribe(Object subscriber, SubscriberMethod subscriberMethod) {
        // 得到當(dāng)前訂閱了事件的方法的參數(shù)類型
        Class<?> eventType = subscriberMethod.eventType;
        // Subscription類保存了要注冊(cè)的類對(duì)象以及當(dāng)前的subscriberMethod
        Subscription newSubscription = new Subscription(subscriber, subscriberMethod);
        // subscriptionsByEventType是一個(gè)HashMap横媚,保存了以eventType為key,Subscription對(duì)象集合為value的鍵值對(duì)
        // 先查找subscriptionsByEventType是否存在以當(dāng)前eventType為key的值
        CopyOnWriteArrayList<Subscription> subscriptions = subscriptionsByEventType.get(eventType);
        // 如果不存在,則創(chuàng)建一個(gè)subscriptions涣楷,并保存到subscriptionsByEventType
        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);
            }
        }
        // 添加上邊創(chuàng)建的newSubscription對(duì)象到subscriptions中
        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;
            }
        }
        // typesBySubscribere也是一個(gè)HashMap分唾,保存了以當(dāng)前要注冊(cè)類的對(duì)象為key,注冊(cè)類中訂閱事件的方法的參數(shù)類型的集合為value的鍵值對(duì)
        // 查找是否存在對(duì)應(yīng)的參數(shù)類型集合
        List<Class<?>> subscribedEvents = typesBySubscriber.get(subscriber);
        // 不存在則創(chuàng)建一個(gè)subscribedEvents狮斗,并保存到typesBySubscriber
        if (subscribedEvents == null) {
            subscribedEvents = new ArrayList<>();
            typesBySubscriber.put(subscriber, subscribedEvents);
        }
        // 保存當(dāng)前訂閱了事件的方法的參數(shù)類型
        subscribedEvents.add(eventType);
        // 粘性事件相關(guān)的绽乔,后邊具體分析
        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);
            }
        }
    }

這就是注冊(cè)的核心流程,所以subscribe()方法主要是得到了subscriptionsByEventType碳褒、typesBySubscriber兩個(gè) HashMap折砸。我們?cè)诎l(fā)送事件的時(shí)候要用到subscriptionsByEventType看疗,完成事件的處理。當(dāng)取消 EventBus 注冊(cè)的時(shí)候要用到typesBySubscriber睦授、subscriptionsByEventType两芳,完成相關(guān)資源的釋放。

三去枷、取消注冊(cè)

接下來(lái)看怖辆,EventBus 如何取消注冊(cè):

EventBus.getDefault().unregister(this);

核心的方法就是unregister()

public synchronized void unregister(Object subscriber) {
        // 得到當(dāng)前注冊(cè)類對(duì)象 對(duì)應(yīng)的 訂閱事件方法的參數(shù)類型 的集合
        List<Class<?>> subscribedTypes = typesBySubscriber.get(subscriber);
        if (subscribedTypes != null) {
            // 遍歷參數(shù)類型集合,釋放之前緩存的當(dāng)前類中的Subscription
            for (Class<?> eventType : subscribedTypes) {
                unsubscribeByEventType(subscriber, eventType);
            }
            // 刪除以subscriber為key的鍵值對(duì)
            typesBySubscriber.remove(subscriber);
        } else {
            logger.log(Level.WARNING, "Subscriber to unregister was not registered before: " + subscriber.getClass());
        }
    }

內(nèi)容很簡(jiǎn)單删顶,繼續(xù)看unsubscribeByEventType()方法:

private void unsubscribeByEventType(Object subscriber, Class<?> eventType) {
        // 得到當(dāng)前參數(shù)類型對(duì)應(yīng)的Subscription集合
        List<Subscription> subscriptions = subscriptionsByEventType.get(eventType);
        if (subscriptions != null) {
            int size = subscriptions.size();
            // 遍歷Subscription集合
            for (int i = 0; i < size; i++) {
                Subscription subscription = subscriptions.get(i);
                // 如果當(dāng)前subscription對(duì)象對(duì)應(yīng)的注冊(cè)類對(duì)象 和 要取消注冊(cè)的注冊(cè)類對(duì)象相同竖螃,則刪除當(dāng)前subscription對(duì)象
                if (subscription.subscriber == subscriber) {
                    subscription.active = false;
                    subscriptions.remove(i);
                    i--;
                    size--;
                }
            }
        }
    }

所以在unregister()方法中,釋放了typesBySubscriber逗余、subscriptionsByEventType中緩存的資源特咆。

四、發(fā)送事件

當(dāng)發(fā)送一個(gè)事件的時(shí)候录粱,我們可以通過(guò)如下方式:

EventBus.getDefault().post("Hello World!")

可以看到腻格,發(fā)送事件就是通過(guò)post()方法完成的:

public void post(Object event) {
        // currentPostingThreadState是一個(gè)PostingThreadState類型的ThreadLocal
        // PostingThreadState類保存了事件隊(duì)列和線程模式等信息
        PostingThreadState postingState = currentPostingThreadState.get();
        List<Object> eventQueue = postingState.eventQueue;
        // 將要發(fā)送的事件添加到事件隊(duì)列
        eventQueue.add(event);
        // isPosting默認(rèn)為false
        if (!postingState.isPosting) {
            // 是否為主線程
            postingState.isMainThread = isMainThread();
            postingState.isPosting = true;
            if (postingState.canceled) {
                throw new EventBusException("Internal error. Abort state was not reset");
            }
            try {
                // 遍歷事件隊(duì)列
                while (!eventQueue.isEmpty()) {
                    // 發(fā)送單個(gè)事件
                    // eventQueue.remove(0),從事件隊(duì)列移除事件
                    postSingleEvent(eventQueue.remove(0), postingState);
                }
            } finally {
                postingState.isPosting = false;
                postingState.isMainThread = false;
            }
        }
    }

所以post()方法先將發(fā)送的事件保存的事件隊(duì)列啥繁,然后通過(guò)循環(huán)出隊(duì)列菜职,將事件交給postSingleEvent()方法處理:

private void postSingleEvent(Object event, PostingThreadState postingState) throws Error {
        Class<?> eventClass = event.getClass();
        boolean subscriptionFound = false;
        // eventInheritance默認(rèn)為true,表示是否向上查找事件的父類
        if (eventInheritance) {
            // 查找當(dāng)前事件類型的Class旗闽,連同當(dāng)前事件類型的Class保存到集合
            List<Class<?>> eventTypes = lookupAllEventTypes(eventClass);
            int countTypes = eventTypes.size();
            // 遍歷Class集合些楣,繼續(xù)處理事件
            for (int h = 0; h < countTypes; h++) {
                Class<?> clazz = eventTypes.get(h);
                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));
            }
        }
    }

postSingleEvent()方法中,根據(jù)eventInheritance屬性宪睹,決定是否向上遍歷事件的父類型愁茁,然后用postSingleEventForEventType()方法進(jìn)一步處理事件:

private boolean postSingleEventForEventType(Object event, PostingThreadState postingState, Class<?> eventClass) {
        CopyOnWriteArrayList<Subscription> subscriptions;
        synchronized (this) {
            // 獲取事件類型對(duì)應(yīng)的Subscription集合
            subscriptions = subscriptionsByEventType.get(eventClass);
        }
        // 如果已訂閱了對(duì)應(yīng)類型的事件
        if (subscriptions != null && !subscriptions.isEmpty()) {
            for (Subscription subscription : subscriptions) {
                // 記錄事件
                postingState.event = event;
                // 記錄對(duì)應(yīng)的subscription
                postingState.subscription = subscription;
                boolean aborted = false;
                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;
    }

postSingleEventForEventType()方法核心就是遍歷發(fā)送的事件類型對(duì)應(yīng)的Subscription集合,然后調(diào)用postToSubscription()方法處理事件亭病。

五鹅很、處理事件

接著上邊的繼續(xù)分析,postToSubscription()內(nèi)部會(huì)根據(jù)訂閱事件方法的線程模式罪帖,間接或直接的以發(fā)送的事件為參數(shù)促煮,通過(guò)反射執(zhí)行訂閱事件的方法。

private void postToSubscription(Subscription subscription, Object event, boolean isMainThread) {
        // 判斷訂閱事件方法的線程模式
        switch (subscription.subscriberMethod.threadMode) {
            // 默認(rèn)的線程模式整袁,在那個(gè)線程發(fā)送事件就在那個(gè)線程處理事件
            case POSTING:
                invokeSubscriber(subscription, event);
                break;
            // 在主線程處理事件
            case MAIN:
                // 如果在主線程發(fā)送事件菠齿,則直接在主線程通過(guò)反射處理事件
                if (isMainThread) {
                    invokeSubscriber(subscription, event);
                } else {
                     // 如果是在子線程發(fā)送事件,則將事件入隊(duì)列坐昙,通過(guò)Handler切換到主線程執(zhí)行處理事件
                    // mainThreadPoster 不為空
                    mainThreadPoster.enqueue(subscription, event);
                }
                break;
            // 無(wú)論在那個(gè)線程發(fā)送事件绳匀,都先將事件入隊(duì)列,然后通過(guò) Handler 切換到主線程,依次處理事件疾棵。
            // mainThreadPoster 不為空
            case MAIN_ORDERED:
                if (mainThreadPoster != null) {
                    mainThreadPoster.enqueue(subscription, event);
                } else {
                    invokeSubscriber(subscription, event);
                }
                break;
            case BACKGROUND:
                // 如果在主線程發(fā)送事件戈钢,則先將事件入隊(duì)列,然后通過(guò)線程池依次處理事件
                if (isMainThread) {
                    backgroundPoster.enqueue(subscription, event);
                } else {
                    // 如果在子線程發(fā)送事件是尔,則直接在發(fā)送事件的線程通過(guò)反射處理事件
                    invokeSubscriber(subscription, event);
                }
                break;
            // 無(wú)論在那個(gè)線程發(fā)送事件殉了,都將事件入隊(duì)列,然后通過(guò)線程池處理拟枚。
            case ASYNC:
                asyncPoster.enqueue(subscription, event);
                break;
            default:
                throw new IllegalStateException("Unknown thread mode: " + subscription.subscriberMethod.threadMode);
        }
    }

可以看到薪铜,postToSubscription()方法就是根據(jù)訂閱事件方法的線程模式、以及發(fā)送事件的線程來(lái)判斷如何處理事件恩溅,至于處理方式主要有兩種:
一種是在相應(yīng)線程直接通過(guò)invokeSubscriber()方法痕囱,用反射來(lái)執(zhí)行訂閱事件的方法,這樣發(fā)送出去的事件就被訂閱者接收并做相應(yīng)處理了:

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

另外一種是先將事件入隊(duì)列(其實(shí)底層是一個(gè)List)暴匠,然后做進(jìn)一步處理,我們以mainThreadPoster.enqueue(subscription, event)為例簡(jiǎn)單的分析下傻粘,其中mainThreadPosterHandlerPoster類的一個(gè)實(shí)例每窖,來(lái)看該類的主要實(shí)現(xiàn):

public class HandlerPoster extends Handler implements Poster {
    private final PendingPostQueue queue;
    private boolean handlerActive;
    ......
    public void enqueue(Subscription subscription, Object event) {
        // 用subscription和event封裝一個(gè)PendingPost對(duì)象
        PendingPost pendingPost = PendingPost.obtainPendingPost(subscription, event);
        synchronized (this) {
            // 入隊(duì)列
            queue.enqueue(pendingPost);
            if (!handlerActive) {
                handlerActive = true;
                // 發(fā)送開始處理事件的消息,handleMessage()方法將被執(zhí)行弦悉,完成從子線程到主線程的切換
                if (!sendMessage(obtainMessage())) {
                    throw new EventBusException("Could not send handler message");
                }
            }
        }
    }

    @Override
    public void handleMessage(Message msg) {
        boolean rescheduled = false;
        try {
            long started = SystemClock.uptimeMillis();
            // 死循環(huán)遍歷隊(duì)列
            while (true) {
                // 出隊(duì)列
                PendingPost pendingPost = queue.poll();
                ......
                // 進(jìn)一步處理pendingPost
                eventBus.invokeSubscriber(pendingPost);
                ......
            }
        } finally {
            handlerActive = rescheduled;
        }
    }
}

所以HandlerPosterenqueue()方法主要就是將subscription窒典、event對(duì)象封裝成一個(gè)PendingPost對(duì)象,然后保存到隊(duì)列里稽莉,之后通過(guò)Handler切換到主線程瀑志,在handleMessage()方法將中將PendingPost對(duì)象循環(huán)出隊(duì)列,交給invokeSubscriber()方法進(jìn)一步處理:

void invokeSubscriber(PendingPost pendingPost) {
        Object event = pendingPost.event;
        Subscription subscription = pendingPost.subscription;
        // 釋放pendingPost引用的資源
        PendingPost.releasePendingPost(pendingPost);
        if (subscription.active) {
            // 用反射來(lái)執(zhí)行訂閱事件的方法
            invokeSubscriber(subscription, event);
        }
    }

這個(gè)方法很簡(jiǎn)單污秆,主要就是從pendingPost中取出之前保存的event劈猪、subscription,然后用反射來(lái)執(zhí)行訂閱事件的方法良拼,又回到了第一種處理方式战得。所以mainThreadPoster.enqueue(subscription, event)的核心就是先將將事件入隊(duì)列,然后通過(guò)Handler從子線程切換到主線程中去處理事件庸推。

backgroundPoster.enqueue()asyncPoster.enqueue也類似常侦,內(nèi)部都是先將事件入隊(duì)列,然后再出隊(duì)列贬媒,但是會(huì)通過(guò)線程池去進(jìn)一步處理事件聋亡。

六、粘性事件

一般情況际乘,我們使用 EventBus 都是準(zhǔn)備好訂閱事件的方法坡倔,然后注冊(cè)事件,最后在發(fā)送事件,即要先有事件的接收者致讥。但粘性事件卻恰恰相反仅仆,我們可以先發(fā)送事件,后續(xù)再準(zhǔn)備訂閱事件的方法垢袱、注冊(cè)事件墓拜。

由于這種差異,我們分析粘性事件原理時(shí)请契,先從事件發(fā)送開始咳榜,發(fā)送一個(gè)粘性事件通過(guò)如下方式:

EventBus.getDefault().postSticky("Hello World!");

來(lái)看postSticky()方法是如何實(shí)現(xiàn)的:

public void postSticky(Object event) {
        synchronized (stickyEvents) {
            stickyEvents.put(event.getClass(), event);
        }
        post(event);
    }

postSticky()方法主要做了兩件事,先將事件類型和對(duì)應(yīng)事件保存到stickyEvents中爽锥,方便后續(xù)使用涌韩;然后執(zhí)行post(event)繼續(xù)發(fā)送事件,這個(gè)post()方法就是之前發(fā)送的post()方法氯夷。所以臣樱,如果在發(fā)送粘性事件前,已經(jīng)有了對(duì)應(yīng)類型事件的訂閱者腮考,及時(shí)它是非粘性的雇毫,依然可以接收到發(fā)送出的粘性事件。

發(fā)送完粘性事件后踩蔚,再準(zhǔn)備訂閱粘性事件的方法棚放,并完成注冊(cè)。核心的注冊(cè)事件流程還是我們之前的register()方法中的subscribe()方法馅闽,前邊分析subscribe()方法時(shí)飘蚯,有一段沒(méi)有分析的代碼,就是用來(lái)處理粘性事件的:

private void subscribe(Object subscriber, SubscriberMethod subscriberMethod) {
        ......
        ......
        ......
        // 如果當(dāng)前訂閱事件的方法的Subscribe注解的sticky屬性為true福也,即該方法可接受粘性事件
        if (subscriberMethod.sticky) {
            // 默認(rèn)為true局骤,表示是否向上查找事件的父類
            if (eventInheritance) {
                // stickyEvents就是發(fā)送粘性事件時(shí),保存了事件類型和對(duì)應(yīng)事件
                Set<Map.Entry<Class<?>, Object>> entries = stickyEvents.entrySet();
                for (Map.Entry<Class<?>, Object> entry : entries) {
                    Class<?> candidateEventType = entry.getKey();
                    // 如果candidateEventType是eventType的子類或
                    if (eventType.isAssignableFrom(candidateEventType)) {
                        // 獲得對(duì)應(yīng)的事件
                        Object stickyEvent = entry.getValue();
                        // 處理粘性事件
                        checkPostStickyEventToSubscription(newSubscription, stickyEvent);
                    }
                }
            } else {
                Object stickyEvent = stickyEvents.get(eventType);
                checkPostStickyEventToSubscription(newSubscription, stickyEvent);
            }
        }
    }

可以看到暴凑,處理粘性事件就是在 EventBus 注冊(cè)時(shí)庄涡,遍歷stickyEvents,如果當(dāng)前要注冊(cè)的事件訂閱方法是粘性的搬设,并且該方法接收的事件類型和stickyEvents中某個(gè)事件類型相同或者是其父類穴店,則取出stickyEvents中對(duì)應(yīng)事件類型的具體事件,做進(jìn)一步處理拿穴。繼續(xù)看checkPostStickyEventToSubscription()處理方法:

private void checkPostStickyEventToSubscription(Subscription newSubscription, Object stickyEvent) {
        if (stickyEvent != null) {
            postToSubscription(newSubscription, stickyEvent, isMainThread());
        }
    }

最終還是通過(guò)postToSubscription()方法完成粘性事件的處理泣洞,這就是粘性事件的整個(gè)處理流程。

七默色、Subscriber Index

回顧之前分析的 EventBus 注冊(cè)事件流程球凰,主要是在項(xiàng)目運(yùn)行時(shí)通過(guò)反射來(lái)查找訂事件的方法信息,這也是默認(rèn)的實(shí)現(xiàn),如果項(xiàng)目中有大量的訂閱事件的方法呕诉,必然會(huì)對(duì)項(xiàng)目運(yùn)行時(shí)的性能產(chǎn)生影響缘厢。其實(shí)除了在項(xiàng)目運(yùn)行時(shí)通過(guò)反射查找訂閱事件的方法信息,EventBus 還提供了在項(xiàng)目編譯時(shí)通過(guò)注解處理器查找訂閱事件方法信息的方式甩挫,生成一個(gè)輔助的索引類來(lái)保存這些信息贴硫,這個(gè)索引類就是Subscriber Index,其實(shí)和 ButterKnife 的原理類似伊者。

要在項(xiàng)目編譯時(shí)查找訂閱事件的方法信息英遭,首先要在 app 的 build.gradle 中加入如下配置:

android {
    defaultConfig {
        javaCompileOptions {
            annotationProcessorOptions {
                // 根據(jù)項(xiàng)目實(shí)際情況,指定輔助索引類的名稱和包名
                arguments = [ eventBusIndex : 'com.shh.sometest.MyEventBusIndex' ]
            }
        }
    }
}
dependencies {
    compile 'org.greenrobot:eventbus:3.1.1'
    // 引入注解處理器
    annotationProcessor 'org.greenrobot:eventbus-annotation-processor:3.1.1'
}

然后在項(xiàng)目的 Application 中添加如下配置亦渗,以生成一個(gè)默認(rèn)的 EventBus 單例:

EventBus.builder().addIndex(new MyEventBusIndex()).installDefaultEventBus();

之后的用法就和我們平時(shí)使用 EventBus 一樣了挖诸。當(dāng)項(xiàng)目編譯時(shí)會(huì)在生成對(duì)應(yīng)的MyEventBusIndex類:

MyEventBusIndex

對(duì)應(yīng)的源碼如下:

public class MyEventBusIndex implements SubscriberInfoIndex {
    private static final Map<Class<?>, SubscriberInfo> SUBSCRIBER_INDEX;

    static {
        SUBSCRIBER_INDEX = new HashMap<Class<?>, SubscriberInfo>();

        putIndex(new SimpleSubscriberInfo(MainActivity.class, true, new SubscriberMethodInfo[] {
            new SubscriberMethodInfo("changeText", String.class),
        }));

    }

    private static void putIndex(SubscriberInfo info) {
        SUBSCRIBER_INDEX.put(info.getSubscriberClass(), info);
    }

    @Override
    public SubscriberInfo getSubscriberInfo(Class<?> subscriberClass) {
        SubscriberInfo info = SUBSCRIBER_INDEX.get(subscriberClass);
        if (info != null) {
            return info;
        } else {
            return null;
        }
    }
}

其中SUBSCRIBER_INDEX是一個(gè)HashMap,保存了當(dāng)前注冊(cè)類的 Class 類型和其中事件訂閱方法的信息法精。

接下來(lái)分析下使用 Subscriber Index 時(shí) EventBus 的注冊(cè)流程多律,我們先分析:

EventBus.builder().addIndex(new MyEventBusIndex()).installDefaultEventBus();

首先創(chuàng)建一個(gè)EventBusBuilder,然后通過(guò)addIndex()方法添加索引類的實(shí)例:

public EventBusBuilder addIndex(SubscriberInfoIndex index) {
        if (subscriberInfoIndexes == null) {
            subscriberInfoIndexes = new ArrayList<>();
        }
        subscriberInfoIndexes.add(index);
        return this;
    }

即把生成的索引類的實(shí)例保存在subscriberInfoIndexes集合中搂蜓,然后用installDefaultEventBus()創(chuàng)建默認(rèn)的 EventBus實(shí)例:

public EventBus installDefaultEventBus() {
        synchronized (EventBus.class) {
            if (EventBus.defaultInstance != null) {
                throw new EventBusException("Default instance already exists." +
                        " It may be only set once before it's used the first time to ensure consistent behavior.");
            }
            EventBus.defaultInstance = build();
            return EventBus.defaultInstance;
        }
    }
public EventBus build() {
        // this 代表當(dāng)前EventBusBuilder對(duì)象
        return new EventBus(this);
    }

即用當(dāng)前EventBusBuilder對(duì)象創(chuàng)建一個(gè) EventBus 實(shí)例狼荞,這樣我們通過(guò)EventBusBuilder配置的 Subscriber Index 也就傳遞到了EventBus實(shí)例中,然后賦值給EventBus的 defaultInstance成員變量洛勉。之前我們?cè)诜治?EventBus 的getDefault()方法時(shí)已經(jīng)見到了defaultInstance

public static EventBus getDefault() {
        if (defaultInstance == null) {
            synchronized (EventBus.class) {
                if (defaultInstance == null) {
                    defaultInstance = new EventBus();
                }
            }
        }
        return defaultInstance;
    }

所以在 Application 中生成了 EventBus 的默認(rèn)單例,這樣就保證了在項(xiàng)目其它地方執(zhí)行EventBus.getDefault()就能得到唯一的 EventBus 實(shí)例如迟!之前在分析注冊(cè)流程時(shí)有一個(gè)
方法findUsingInfo()

private List<SubscriberMethod> findUsingInfo(Class<?> subscriberClass) {
        FindState findState = prepareFindState();
        findState.initForSubscriber(subscriberClass);
        while (findState.clazz != null) {
            // 查找SubscriberInfo
            findState.subscriberInfo = getSubscriberInfo(findState);
            // 條件成立
            if (findState.subscriberInfo != null) {
                // 獲得當(dāng)前注冊(cè)類中所有訂閱了事件的方法
                SubscriberMethod[] array = findState.subscriberInfo.getSubscriberMethods();
                for (SubscriberMethod subscriberMethod : array) {
                    // findState.checkAdd()之前已經(jīng)分析過(guò)了收毫,即是否在FindState的anyMethodByEventType已經(jīng)添加過(guò)以當(dāng)前eventType為key的鍵值對(duì),沒(méi)添加過(guò)返回true
                    if (findState.checkAdd(subscriberMethod.method, subscriberMethod.eventType)) {
                        // 將subscriberMethod對(duì)象添加到subscriberMethods集合
                        findState.subscriberMethods.add(subscriberMethod);
                    }
                }
            } else {
                findUsingReflectionInSingleClass(findState);
            }
            findState.moveToSuperclass();
        }
        return getMethodsAndRelease(findState);
    }

由于我們現(xiàn)在使用了 Subscriber Index 所以不會(huì)通過(guò)findUsingReflectionInSingleClass()來(lái)反射解析訂閱事件的方法殷勘。我們重點(diǎn)來(lái)看getSubscriberInfo()都做了些什么:

private SubscriberInfo getSubscriberInfo(FindState findState) {
        // 該條件不成立
        if (findState.subscriberInfo != null && findState.subscriberInfo.getSuperSubscriberInfo() != null) {
            SubscriberInfo superclassInfo = findState.subscriberInfo.getSuperSubscriberInfo();
            if (findState.clazz == superclassInfo.getSubscriberClass()) {
                return superclassInfo;
            }
        }
        // 該條件成立
        if (subscriberInfoIndexes != null) {
            // 遍歷索引類實(shí)例集合
            for (SubscriberInfoIndex index : subscriberInfoIndexes) {
                // 根據(jù)注冊(cè)類的 Class 類查找SubscriberInfo
                SubscriberInfo info = index.getSubscriberInfo(findState.clazz);
                if (info != null) {
                    return info;
                }
            }
        }
        return null;
    }

subscriberInfoIndexes就是在前邊addIndex()方法中創(chuàng)建的此再,保存了項(xiàng)目中的索引類實(shí)例,即MyEventBusIndex的實(shí)例玲销,繼續(xù)看索引類的getSubscriberInfo()方法输拇,來(lái)到了MyEventBusIndex類中:

@Override
    public SubscriberInfo getSubscriberInfo(Class<?> subscriberClass) {
        SubscriberInfo info = SUBSCRIBER_INDEX.get(subscriberClass);
        if (info != null) {
            return info;
        } else {
            return null;
        }
    }

即根據(jù)注冊(cè)類的 Class 類型從 SUBSCRIBER_INDEX 查找對(duì)應(yīng)的SubscriberInfo,如果我們?cè)谧?cè)類中定義了訂閱事件的方法贤斜,則 info不為空策吠,進(jìn)而上邊findUsingInfo()方法中findState.subscriberInfo != null成立,到這里主要的內(nèi)容就分析完了瘩绒,其它的和之前的注冊(cè)流程一樣猴抹。

所以 Subscriber Index 的核心就是項(xiàng)目編譯時(shí)使用注解處理器生成保存事件訂閱方法信息的索引類,然后項(xiàng)目運(yùn)行時(shí)將索引類實(shí)例設(shè)置到 EventBus 中锁荔,這樣當(dāng)注冊(cè) EventBus 時(shí)蟀给,從索引類取出當(dāng)前注冊(cè)類對(duì)應(yīng)的事件訂閱方法信息,以完成最終的注冊(cè),避免了運(yùn)行時(shí)反射處理的過(guò)程跋理,所以在性能上會(huì)有質(zhì)的提高择克。項(xiàng)目中可以根據(jù)實(shí)際的需求決定是否使用 Subscriber Index。

八前普、小結(jié)

結(jié)合上邊的分析肚邢,我們可以總結(jié)出registerpost汁政、unregister的核心流程:

register
post
unregister

到這里 EventBus 幾個(gè)重要的流程就分析完了道偷,整體的設(shè)計(jì)思路還是值得我們學(xué)習(xí)的。和 Android 自帶的廣播相比记劈,使用簡(jiǎn)單勺鸦、同時(shí)又兼顧了性能。但如果在項(xiàng)目中濫用的話目木,各種邏輯交叉在一起换途,也可能會(huì)給后期的維護(hù)帶來(lái)困難。

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末刽射,一起剝皮案震驚了整個(gè)濱河市军拟,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌誓禁,老刑警劉巖懈息,帶你破解...
    沈念sama閱讀 216,402評(píng)論 6 499
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場(chǎng)離奇詭異摹恰,居然都是意外死亡辫继,警方通過(guò)查閱死者的電腦和手機(jī),發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 92,377評(píng)論 3 392
  • 文/潘曉璐 我一進(jìn)店門俗慈,熙熙樓的掌柜王于貴愁眉苦臉地迎上來(lái)姑宽,“玉大人,你說(shuō)我怎么就攤上這事闺阱∨诔担” “怎么了?”我有些...
    開封第一講書人閱讀 162,483評(píng)論 0 353
  • 文/不壞的土叔 我叫張陵酣溃,是天一觀的道長(zhǎng)瘦穆。 經(jīng)常有香客問(wèn)我,道長(zhǎng)赊豌,這世上最難降的妖魔是什么难审? 我笑而不...
    開封第一講書人閱讀 58,165評(píng)論 1 292
  • 正文 為了忘掉前任,我火速辦了婚禮亿絮,結(jié)果婚禮上告喊,老公的妹妹穿的比我還像新娘麸拄。我一直安慰自己,他們只是感情好黔姜,可當(dāng)我...
    茶點(diǎn)故事閱讀 67,176評(píng)論 6 388
  • 文/花漫 我一把揭開白布拢切。 她就那樣靜靜地躺著,像睡著了一般秆吵。 火紅的嫁衣襯著肌膚如雪淮椰。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 51,146評(píng)論 1 297
  • 那天纳寂,我揣著相機(jī)與錄音主穗,去河邊找鬼。 笑死毙芜,一個(gè)胖子當(dāng)著我的面吹牛忽媒,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播腋粥,決...
    沈念sama閱讀 40,032評(píng)論 3 417
  • 文/蒼蘭香墨 我猛地睜開眼晦雨,長(zhǎng)吁一口氣:“原來(lái)是場(chǎng)噩夢(mèng)啊……” “哼!你這毒婦竟也來(lái)了隘冲?” 一聲冷哼從身側(cè)響起闹瞧,我...
    開封第一講書人閱讀 38,896評(píng)論 0 274
  • 序言:老撾萬(wàn)榮一對(duì)情侶失蹤,失蹤者是張志新(化名)和其女友劉穎展辞,沒(méi)想到半個(gè)月后奥邮,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 45,311評(píng)論 1 310
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡罗珍,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 37,536評(píng)論 2 332
  • 正文 我和宋清朗相戀三年洽腺,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片靡砌。...
    茶點(diǎn)故事閱讀 39,696評(píng)論 1 348
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡已脓,死狀恐怖珊楼,靈堂內(nèi)的尸體忽然破棺而出通殃,到底是詐尸還是另有隱情,我是刑警寧澤厕宗,帶...
    沈念sama閱讀 35,413評(píng)論 5 343
  • 正文 年R本政府宣布画舌,位于F島的核電站,受9級(jí)特大地震影響已慢,放射性物質(zhì)發(fā)生泄漏曲聂。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,008評(píng)論 3 325
  • 文/蒙蒙 一佑惠、第九天 我趴在偏房一處隱蔽的房頂上張望朋腋。 院中可真熱鬧齐疙,春花似錦、人聲如沸旭咽。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,659評(píng)論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)穷绵。三九已至轿塔,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間仲墨,已是汗流浹背勾缭。 一陣腳步聲響...
    開封第一講書人閱讀 32,815評(píng)論 1 269
  • 我被黑心中介騙來(lái)泰國(guó)打工, 沒(méi)想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留目养,地道東北人俩由。 一個(gè)月前我還...
    沈念sama閱讀 47,698評(píng)論 2 368
  • 正文 我出身青樓,卻偏偏與公主長(zhǎng)得像混稽,于是被迫代替她去往敵國(guó)和親采驻。 傳聞我的和親對(duì)象是個(gè)殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 44,592評(píng)論 2 353

推薦閱讀更多精彩內(nèi)容

  • 我從去年開始使用 RxJava 匈勋,到現(xiàn)在一年多了礼旅。今年加入了 Flipboard 后,看到 Flipboard 的...
    Jason_andy閱讀 5,468評(píng)論 7 62
  • EventBus用法及源碼解析目錄介紹1.EventBus簡(jiǎn)介1.1 EventBus的三要素1.2 EventB...
    楊充211閱讀 1,892評(píng)論 0 4
  • 博文出處:EventBus源碼解析洽洁,歡迎大家關(guān)注我的博客痘系,謝謝! 0001B 時(shí)近年末饿自,但是也沒(méi)閑著汰翠。最近正好在看...
    俞其榮閱讀 1,301評(píng)論 1 16
  • 前言我從去年開始使用 RxJava ,到現(xiàn)在一年多了昭雌。今年加入了 Flipboard 后复唤,看到 Flipboard...
    占導(dǎo)zqq閱讀 9,164評(píng)論 6 151
  • “你覺得最理想的愛情生活是怎么樣子的呢”“兩個(gè)人一條狗吧”“我希望是一屋兩人三餐四季” 家的繁體字是“傢”。從會(huì)意...
    漠郴江閱讀 1,425評(píng)論 0 2