EventBus 源碼解析

轉(zhuǎn)載自:http://www.reibang.com/p/83e44261e095

注冊(cè)register

    public void register(Object subscriber) {
        // 首先會(huì)獲取注冊(cè)的對(duì)象的類型
        Class<?> subscriberClass = subscriber.getClass();
        // 然后獲取注冊(cè)的對(duì)象的訂閱方法
        List<SubscriberMethod> subscriberMethods = subscriberMethodFinder.findSubscriberMethods(subscriberClass);
        // 對(duì)當(dāng)前實(shí)例加鎖,并不斷執(zhí)行監(jiān)聽(tīng)的邏輯
        synchronized (this) {
            for (SubscriberMethod subscriberMethod : subscriberMethods) {
                // 對(duì)訂閱方法進(jìn)行注冊(cè)
                subscribe(subscriber, subscriberMethod);
            }
        }
    }

查找訂閱者的訂閱方法findSubscriberMethods

    List<SubscriberMethod> findSubscriberMethods(Class<?> subscriberClass) {
        // 這里首先從緩存當(dāng)中嘗試去取該訂閱者的訂閱方法
        List<SubscriberMethod> subscriberMethods = METHOD_CACHE.get(subscriberClass);
        if (subscriberMethods != null) {
            return subscriberMethods;
        }
        // 當(dāng)緩存中沒(méi)有找到該觀察者的訂閱方法的時(shí)候使用下面的兩種方法獲取方法信息
        if (ignoreGeneratedIndex) {
            //是否忽略注解器生成的MyEventBusIndex
            subscriberMethods = findUsingReflection(subscriberClass);
        } else {
            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;
        }
    }
  • 這里我們先從緩存當(dāng)中嘗試獲取某個(gè)觀察者中的所有訂閱方法,如果沒(méi)有可用緩存的話就從該類中查找訂閱方法哗魂,并在返回結(jié)果之前將這些方法信息放置到緩存當(dāng)中。這里的ignoreGeneratedIndex參數(shù)表示是否忽略注解器生成的MyEventBusIndex组题,該值默認(rèn)為false崔列。

獲取訂閱方法findUsingInfo

    private List<SubscriberMethod> findUsingInfo(Class<?> subscriberClass) {
        // 這里通過(guò)FindState對(duì)象來(lái)存儲(chǔ)找到的方法信息
        FindState findState = prepareFindState();
        findState.initForSubscriber(subscriberClass);
        // 這里是一個(gè)循環(huán)操作峻呕,會(huì)從當(dāng)前類開(kāi)始遍歷該類的所有父類
        while (findState.clazz != null) {
            // 獲取訂閱者信息
            findState.subscriberInfo = getSubscriberInfo(findState); //#1
            // 如果使用了MyEventBusIndex,將會(huì)進(jìn)入到這里并獲取訂閱方法信息
            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 {
                // 未使用MyEventBusIndex將會(huì)進(jìn)入這里使用反射獲取方法信息
                findUsingReflectionInSingleClass(findState);  //#2
            }
            // 將findState.clazz設(shè)置為當(dāng)前的findState.clazz的父類
            findState.moveToSuperclass();
        }
        return getMethodsAndRelease(findState);
    }

從當(dāng)前訂閱者類開(kāi)始直到它最頂層的父類進(jìn)行遍歷來(lái)獲取訂閱方法信息。這里在循環(huán)的內(nèi)部會(huì)根據(jù)我們是否使用了MyEventBusIndex走兩條路線斤寇,對(duì)于我們沒(méi)有使用它的娘锁,會(huì)直接使用反射來(lái)獲取訂閱方法信息间雀,即進(jìn)入2

使用反射從訂閱者中得到訂閱方法findUsingReflectionInSingleClass

    private void findUsingReflectionInSingleClass(FindState findState) {
        Method[] methods;
        try {
            // 獲取該類中聲明的所有方法
            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;
        }
        // 對(duì)方法進(jìn)行遍歷判斷
        for (Method method : methods) {
            int modifiers = method.getModifiers();
            // 這里會(huì)對(duì)方法的修飾符進(jìn)行校驗(yàn)
            if ((modifiers & Modifier.PUBLIC) != 0 && (modifiers & MODIFIERS_IGNORE) == 0) {
                Class<?>[] parameterTypes = method.getParameterTypes();
                // 判斷方法的參數(shù)量,只處理一個(gè)參數(shù)
                if (parameterTypes.length == 1) {
                    // 獲取方法的注解连锯,用來(lái)從注解中獲取注解的聲明信息
                    Subscribe subscribeAnnotation = method.getAnnotation(Subscribe.class);
                    if (subscribeAnnotation != null) {
                        // 獲取該方法的第一個(gè)參數(shù)
                        Class<?> eventType = parameterTypes[0];
                        if (findState.checkAdd(method, eventType)) {
                            ThreadMode threadMode = subscribeAnnotation.threadMode();
                            // 最終將封裝之后的方法塞入到列表中
                            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");
            }
        }
    }
  • 對(duì)當(dāng)前類中聲明的所有方法進(jìn)行校驗(yàn)驳规,并將符合要求的方法的信息封裝成一個(gè)SubscriberMethod對(duì)象塞到列表中医男。

注冊(cè)訂閱事件subscribe

    private void subscribe(Object subscriber, SubscriberMethod subscriberMethod) {
        Class<?> eventType = subscriberMethod.eventType;
        // 將所有的觀察者和訂閱方法封裝成一個(gè)Subscription對(duì)象 #1
        Subscription newSubscription = new Subscription(subscriber, subscriberMethod);
        // 嘗試從緩存中根據(jù)事件類型來(lái)獲取所有的Subscription對(duì)象
        CopyOnWriteArrayList<Subscription> subscriptions = subscriptionsByEventType.get(eventType);
        if (subscriptions == null) {
            // 指定的事件類型沒(méi)有對(duì)應(yīng)的觀察對(duì)象的時(shí)候
            subscriptions = new CopyOnWriteArrayList<>();
            subscriptionsByEventType.put(eventType, subscriptions);
        } else {
            if (subscriptions.contains(newSubscription)) {
                throw new EventBusException("Subscriber " + subscriber.getClass() + " already registered to event "
                        + eventType);
            }
        }
        // 這里會(huì)根據(jù)新加入的方法的優(yōu)先級(jí)決定插入到隊(duì)列中的位置
        int size = subscriptions.size(); //#2
        for (int i = 0; i <= size; i++) {
            if (i == size || subscriberMethod.priority > subscriptions.get(i).subscriberMethod.priority) {
                subscriptions.add(i, newSubscription);
                break;
            }
        }
        // 這里又會(huì)從“訂閱者-事件類型”列表中嘗試獲取該訂閱者對(duì)應(yīng)的所有事件類型
        List<Class<?>> subscribedEvents = typesBySubscriber.get(subscriber); //#3
        if (subscribedEvents == null) {
            subscribedEvents = new ArrayList<>();
            typesBySubscriber.put(subscriber, subscribedEvents);
        }
        subscribedEvents.add(eventType);
        // 如果是黏性事件還要進(jìn)行如下的處理
        if (subscriberMethod.sticky) { //#4
            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();
                        // 這里會(huì)向該觀察者通知所有的黏性事件
                        checkPostStickyEventToSubscription(newSubscription, stickyEvent);
                    }
                }
            } else {
                Object stickyEvent = stickyEvents.get(eventType);
                checkPostStickyEventToSubscription(newSubscription, stickyEvent);
            }
        }
    }

這里涉及到了幾個(gè)集合镀梭,它們是用來(lái)做緩存的刀森,還有就是來(lái)維護(hù)觀察者报账、事件類型和訂閱方法之間的關(guān)系的。注冊(cè)觀察的方法比較長(zhǎng)透罢,我們可以一點(diǎn)一點(diǎn)來(lái)看羽圃。

  • 首先斩郎,會(huì)在代碼1處將觀察者和訂閱方法封裝成一個(gè)Subscription對(duì)象。然后,在2處用到了CopyOnWriteArrayList這個(gè)集合炼幔,它是一種適用于多讀寫(xiě)少場(chǎng)景的數(shù)據(jù)結(jié)構(gòu)乃秀,是一種線程安全的數(shù)組型的數(shù)據(jù)結(jié)構(gòu),主要用來(lái)存儲(chǔ)一個(gè)事件類型所對(duì)應(yīng)的全部的Subscription對(duì)象圆兵。EventBus在這里通過(guò)一個(gè)Map<Class<?>, CopyOnWriteArrayList<Subscription>>類型的哈希表來(lái)維護(hù)這個(gè)映射關(guān)系跺讯。

  • 然后,我們的程序執(zhí)行到2處超凳,在這里會(huì)對(duì)Subscription對(duì)象的列表進(jìn)行遍歷愈污,并根據(jù)訂閱方法的優(yōu)先級(jí),為當(dāng)前的Subscription對(duì)象尋找一個(gè)合適的位置轮傍。

  • 3的地方主要的邏輯是獲取指定的觀察者對(duì)應(yīng)的全部的觀察事件類型暂雹,這里也是通過(guò)一個(gè)哈希表來(lái)維護(hù)這種映射關(guān)系的。

  • 然后创夜,在代碼4處杭跪,程序會(huì)根據(jù)當(dāng)前的訂閱方法是否是黏性的,來(lái)決定是否將當(dāng)前緩存中的信息發(fā)送給新訂閱的方法驰吓。這里會(huì)通過(guò)checkPostStickyEventToSubscription方法來(lái)發(fā)送信息涧尿,它內(nèi)部的實(shí)現(xiàn)的邏輯和post方法類似。

取消注冊(cè)unregister

public synchronized void unregister(Object subscriber) {
        List<Class<?>> subscribedTypes = typesBySubscriber.get(subscriber);
        if (subscribedTypes != null) {
            for (Class<?> eventType : subscribedTypes) {
                unsubscribeByEventType(subscriber, eventType);
            }
            typesBySubscriber.remove(subscriber);
        } else {
            logger.log(Level.WARNING, "Subscriber to unregister was not registered before: " + subscriber.getClass());
        }
    }

取消注冊(cè)的邏輯比較比較簡(jiǎn)單檬贰,基本上就是注冊(cè)操作反過(guò)來(lái)——將當(dāng)前訂閱方法的信息從緩存中踢出來(lái)姑廉,我們不再進(jìn)行分分析。下面我們分析另一個(gè)比較重要的地方偎蘸,即發(fā)送事件相關(guān)的邏輯

通知post

    public void post(Object event) {
        // 這里從線程局部變量中取出當(dāng)前線程的狀態(tài)信息
        PostingThreadState postingState = currentPostingThreadState.get();
        List<Object> eventQueue = postingState.eventQueue;
        // 將當(dāng)前要發(fā)送的事件加入到隊(duì)列中
        eventQueue.add(event);

        if (!postingState.isPosting) {
            postingState.isMainThread = isMainThread();
            postingState.isPosting = true;
            if (postingState.canceled) {
                throw new EventBusException("Internal error. Abort state was not reset");
            }
            try {
                // 不斷循環(huán)來(lái)發(fā)送事件
                while (!eventQueue.isEmpty()) {
                    postSingleEvent(eventQueue.remove(0), postingState); // #1
                }
            } finally {
                postingState.isPosting = false;
                postingState.isMainThread = false;
            }
        }
    }
  • 這里的currentPostingThreadState是一個(gè)ThreadLocal類型的變量庄蹋,其中存儲(chǔ)了對(duì)應(yīng)于當(dāng)前線程的PostingThreadState對(duì)象瞬内,該對(duì)象中存儲(chǔ)了當(dāng)前線程對(duì)應(yīng)的事件列表和線程的狀態(tài)信息等。

從上面的代碼中可以看出限书,post方法會(huì)在1處不斷從當(dāng)前線程對(duì)應(yīng)的隊(duì)列中取出事件并進(jìn)行發(fā)布虫蝶。下面我們看以下這里的postSingleEvent方法。

postSingleEvent

    private void postSingleEvent(Object event, PostingThreadState postingState) throws Error {
        Class<?> eventClass = event.getClass();
        boolean subscriptionFound = false;
        if (eventInheritance) {
            // 這里向上查找該事件的所有父類
            List<Class<?>> eventTypes = lookupAllEventTypes(eventClass);
            int countTypes = eventTypes.size();
            for (int h = 0; h < countTypes; h++) {
                Class<?> clazz = eventTypes.get(h);
                // 對(duì)上面的事件進(jìn)行處理
                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));
            }
        }
    }
  • 我們會(huì)根據(jù)eventInheritance的值決定是否要同時(shí)遍歷當(dāng)前事件的所有父類的事件信息并進(jìn)行分發(fā)倦西。如果設(shè)置為true就將執(zhí)行這一操作能真,并最終使用postSingleEventForEventType對(duì)每個(gè)事件類型進(jìn)行處理

postSingleEventForEventType

    private boolean postSingleEventForEventType(Object event, PostingThreadState postingState, Class<?> eventClass) {
        // 獲取指定的事件對(duì)應(yīng)的所有的觀察對(duì)象
        CopyOnWriteArrayList<Subscription> subscriptions;
        synchronized (this) {
            subscriptions = subscriptionsByEventType.get(eventClass);
        }
        if (subscriptions != null && !subscriptions.isEmpty()) {
            // 遍歷觀察對(duì)象,并最終執(zhí)行事件的分發(fā)操作
            for (Subscription subscription : subscriptions) {
                postingState.event = event;
                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;
    }
  • 通過(guò)傳入的事件類型到緩存中取尋找它對(duì)應(yīng)的全部的Subscription扰柠,然后對(duì)得到的Subscription列表進(jìn)行遍歷粉铐,并依次調(diào)用postToSubscription方法執(zhí)行事件的發(fā)布操作。

postToSubscription

下面是postToSubscription方法的代碼卤档,這里我們會(huì)根據(jù)訂閱方法指定的threadMode信息來(lái)執(zhí)行不同的發(fā)布策略

    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 MAIN_ORDERED:
                if (mainThreadPoster != null) {
                    mainThreadPoster.enqueue(subscription, event);
                } else {
                    // temporary: technically not correct as poster not decoupled from subscriber
                    invokeSubscriber(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);
        }
    }

在上面的方法中蝙泼,會(huì)根據(jù)當(dāng)前的線程狀態(tài)和訂閱方法指定的threadMode信息來(lái)決定合適觸發(fā)方法。

  • 這里的invokeSubscriber會(huì)在當(dāng)前線程中立即調(diào)用反射來(lái)觸發(fā)指定的觀察者的訂閱方法劝枣。否則會(huì)根據(jù)具體的情況將事件加入到不同的隊(duì)列中進(jìn)行處理汤踏。

  • 這里的mainThreadPoster最終繼承自Handler,當(dāng)調(diào)用它的enqueue方法的時(shí)候舔腾,它會(huì)發(fā)送一個(gè)事件并在它自身的handleMessage方法中從隊(duì)列中取值并進(jìn)行處理溪胶,從而達(dá)到在主線程中分發(fā)事件的目的。

  • 這里的backgroundPoster實(shí)現(xiàn)了Runnable接口稳诚,它會(huì)在調(diào)用enqueue方法的時(shí)候哗脖,拿到EventBus的ExecutorService實(shí)例,并使用它來(lái)執(zhí)行自己扳还。在它的run方法中會(huì)從隊(duì)列中不斷取值來(lái)進(jìn)行執(zhí)行才避。

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個(gè)濱河市普办,隨后出現(xiàn)的幾起案子工扎,更是在濱河造成了極大的恐慌,老刑警劉巖衔蹲,帶你破解...
    沈念sama閱讀 218,122評(píng)論 6 505
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場(chǎng)離奇詭異呈础,居然都是意外死亡舆驶,警方通過(guò)查閱死者的電腦和手機(jī),發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 93,070評(píng)論 3 395
  • 文/潘曉璐 我一進(jìn)店門(mén)而钞,熙熙樓的掌柜王于貴愁眉苦臉地迎上來(lái)沙廉,“玉大人,你說(shuō)我怎么就攤上這事臼节∏肆辏” “怎么了珊皿?”我有些...
    開(kāi)封第一講書(shū)人閱讀 164,491評(píng)論 0 354
  • 文/不壞的土叔 我叫張陵,是天一觀的道長(zhǎng)巨税。 經(jīng)常有香客問(wèn)我蟋定,道長(zhǎng),這世上最難降的妖魔是什么草添? 我笑而不...
    開(kāi)封第一講書(shū)人閱讀 58,636評(píng)論 1 293
  • 正文 為了忘掉前任驶兜,我火速辦了婚禮,結(jié)果婚禮上远寸,老公的妹妹穿的比我還像新娘抄淑。我一直安慰自己,他們只是感情好驰后,可當(dāng)我...
    茶點(diǎn)故事閱讀 67,676評(píng)論 6 392
  • 文/花漫 我一把揭開(kāi)白布肆资。 她就那樣靜靜地躺著,像睡著了一般灶芝。 火紅的嫁衣襯著肌膚如雪郑原。 梳的紋絲不亂的頭發(fā)上,一...
    開(kāi)封第一講書(shū)人閱讀 51,541評(píng)論 1 305
  • 那天监署,我揣著相機(jī)與錄音颤专,去河邊找鬼。 笑死钠乏,一個(gè)胖子當(dāng)著我的面吹牛栖秕,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播晓避,決...
    沈念sama閱讀 40,292評(píng)論 3 418
  • 文/蒼蘭香墨 我猛地睜開(kāi)眼簇捍,長(zhǎng)吁一口氣:“原來(lái)是場(chǎng)噩夢(mèng)啊……” “哼!你這毒婦竟也來(lái)了俏拱?” 一聲冷哼從身側(cè)響起暑塑,我...
    開(kāi)封第一講書(shū)人閱讀 39,211評(píng)論 0 276
  • 序言:老撾萬(wàn)榮一對(duì)情侶失蹤,失蹤者是張志新(化名)和其女友劉穎锅必,沒(méi)想到半個(gè)月后事格,有當(dāng)?shù)厝嗽跇?shù)林里發(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 45,655評(píng)論 1 314
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡搞隐,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 37,846評(píng)論 3 336
  • 正文 我和宋清朗相戀三年驹愚,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片劣纲。...
    茶點(diǎn)故事閱讀 39,965評(píng)論 1 348
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡逢捺,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出癞季,到底是詐尸還是另有隱情劫瞳,我是刑警寧澤倘潜,帶...
    沈念sama閱讀 35,684評(píng)論 5 347
  • 正文 年R本政府宣布,位于F島的核電站志于,受9級(jí)特大地震影響涮因,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜恨憎,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,295評(píng)論 3 329
  • 文/蒙蒙 一蕊退、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧憔恳,春花似錦瓤荔、人聲如沸。這莊子的主人今日做“春日...
    開(kāi)封第一講書(shū)人閱讀 31,894評(píng)論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)。三九已至程梦,卻和暖如春点把,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背屿附。 一陣腳步聲響...
    開(kāi)封第一講書(shū)人閱讀 33,012評(píng)論 1 269
  • 我被黑心中介騙來(lái)泰國(guó)打工郎逃, 沒(méi)想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留,地道東北人挺份。 一個(gè)月前我還...
    沈念sama閱讀 48,126評(píng)論 3 370
  • 正文 我出身青樓褒翰,卻偏偏與公主長(zhǎng)得像,于是被迫代替她去往敵國(guó)和親匀泊。 傳聞我的和親對(duì)象是個(gè)殘疾皇子优训,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 44,914評(píng)論 2 355