ViewModel之LiveData

  • 概述

    我們知道妈嘹,頁面中引用的數(shù)據(jù)放在ViewModel中,那么當(dāng)ViewModel中的數(shù)據(jù)改變的時候怎樣通知界面呢莽使?我們可以通過持有Activity或者Fragment的實例來實現(xiàn)燎字,但是在Android中組件實例的生命周期通常是易變的,隨時可能銷毀或者更改虽惭,所以按照原則,不能在生命周期長的類中引用生命周期短暫或者不可控的類的實例蛇尚;并且芽唇,ViewModel中通常含有網(wǎng)絡(luò)請求等異步操作,所以線程間異步通信也要考慮UI線程的問題佣蓉,這些問題LiveData都做了很好的處理披摄。

  • 注冊監(jiān)聽

    通過調(diào)用LiveData的observe方法添加回調(diào)接口:

    mViewModel.userId.observe(this, {
        ToastUtil.showDefaultToast(this, it)
    })
    
    public void observe(@NonNull LifecycleOwner owner, @NonNull Observer<? super T> observer) {
        assertMainThread("observe");
        if (owner.getLifecycle().getCurrentState() == DESTROYED) {
            // ignore
            return;
        }
        LifecycleBoundObserver wrapper = new LifecycleBoundObserver(owner, observer);
        ObserverWrapper existing = mObservers.putIfAbsent(observer, wrapper);
        if (existing != null && !existing.isAttachedTo(owner)) {
            throw new IllegalArgumentException("Cannot add the same observer"
                    + " with different lifecycles");
        }
        if (existing != null) {
            return;
        }
          //添加到生命周期組件中
        owner.getLifecycle().addObserver(wrapper);
    }
    

    回調(diào)接口owner是帶有生命周期的組件實例,默認的ComponentActivity和Fragment都實現(xiàn)了這個接口勇凭,LifecycleBoundObserver持有了owner和observer疚膊,相當(dāng)于把他們對應(yīng)在一起,可見虾标,回調(diào)接口實例被添加到了mObservers中寓盗。

  • 關(guān)于數(shù)據(jù)變化的通知

    在LiveData中,有兩個通知改變數(shù)據(jù)的方法:setValue和postValue璧函。但其實postValue最后也是調(diào)用了setValue:

    protected void postValue(T value) {
        boolean postTask;
        synchronized (mDataLock) {
            postTask = mPendingData == NOT_SET;
            mPendingData = value;
        }
        if (!postTask) {
            return;
        }
        ArchTaskExecutor.getInstance().postToMainThread(mPostValueRunnable);
    }
    
     private final Runnable mPostValueRunnable = new Runnable() {
            @SuppressWarnings("unchecked")
            @Override
            public void run() {
                Object newValue;
                synchronized (mDataLock) {
                    newValue = mPendingData;
                    mPendingData = NOT_SET;
                }
                setValue((T) newValue);
            }
        };
    

    可以看到postValue只不過是通過Handler把回調(diào)接口放在隊列中傀蚌,以確保在主線程執(zhí)行。

    所以主要看一下setValue方法:

    @MainThread
    protected void setValue(T value) {
        assertMainThread("setValue");
        mVersion++;
        mData = value;
        dispatchingValue(null);
    }
    

    注意必須是主線程蘸吓,保存數(shù)據(jù)善炫。

    void dispatchingValue(@Nullable ObserverWrapper initiator) {
        if (mDispatchingValue) {
            mDispatchInvalidated = true;
            return;
        }
        mDispatchingValue = true;
        do {
            mDispatchInvalidated = false;
            if (initiator != null) {
                considerNotify(initiator);
                initiator = null;
            } else {
                for (Iterator<Map.Entry<Observer<? super T>, ObserverWrapper>> iterator =
                        mObservers.iteratorWithAdditions(); iterator.hasNext(); ) {
                    considerNotify(iterator.next().getValue());
                    if (mDispatchInvalidated) {
                        break;
                    }
                }
            }
        } while (mDispatchInvalidated);
        mDispatchingValue = false;
    }
    

    注意此時dispatchingValue傳入的是null,所以所有的mObservers中的監(jiān)聽回調(diào)都會被嘗試執(zhí)行:

    private void considerNotify(ObserverWrapper observer) {
        if (!observer.mActive) {
            return;
        }
        // Check latest state b4 dispatch. Maybe it changed state but we didn't get the event yet.
        //
        // we still first check observer.active to keep it as the entrance for events. So even if
        // the observer moved to an active state, if we've not received that event, we better not
        // notify for a more predictable notification order.
        if (!observer.shouldBeActive()) {
            observer.activeStateChanged(false);
            return;
        }
          //決定著數(shù)據(jù)是否已更新
        if (observer.mLastVersion >= mVersion) {
            return;
        }
        observer.mLastVersion = mVersion;
        observer.mObserver.onChanged((T) mData);
    }
    

    上面為什么說嘗試呢库继?因為我這里看到箩艺,在開頭首先會判斷observer的mActive是否為true,不為true則不會執(zhí)行宪萄。注意observer.mLastVersion >= mVersion的判斷艺谆,這是數(shù)據(jù)更新的關(guān)鍵:

    public LiveData(T value) {
        mData = value;
        mVersion = START_VERSION + 1;
    }
    public LiveData() {
        mData = NOT_SET;
        mVersion = START_VERSION;
    }
    @MainThread
    protected void setValue(T value) {
        assertMainThread("setValue");
        mVersion++;
        mData = value;
        dispatchingValue(null);
    }
    

    可以看到只有在帶有初始化值構(gòu)造或者調(diào)用setValue方法更新值之后mVersion才會在原有基礎(chǔ)上+1,所以每次dispatch的時候都要判斷這個版本號,它決定著是否數(shù)據(jù)已更新,如果更新了才會通知回調(diào)方法執(zhí)行珊肃,如果都通過了坝撑,最終執(zhí)行到了我們監(jiān)聽回調(diào)接口的onChanged方法,顯示Toast交煞。

  • 綁定監(jiān)聽的時候也會觸發(fā)監(jiān)聽回調(diào)

    除了setValue和postValue這種手動的通知監(jiān)聽的方式纺座,當(dāng)LiveData調(diào)用observe方法綁定到組件生命周期時也會觸發(fā)監(jiān)聽回調(diào)硝枉,只不過在頁面可見的時候(onStart之后)才會執(zhí)行監(jiān)聽回調(diào)代碼抹估。

    前面注冊監(jiān)聽的時候我們看到杰捂,在observe方法中還有一句owner.getLifecycle().addObserver(wrapper)代碼,用于把回調(diào)接口綁定到生命周期組件的監(jiān)聽回調(diào)集合里棋蚌。

    owner.getLifecycle()方法得到的是當(dāng)前界面組件的LifecycleRegistry,看一下它的addObserver方法:

    @Override
    public void addObserver(@NonNull LifecycleObserver observer) {
        State initialState = mState == DESTROYED ? DESTROYED : INITIALIZED;
        ObserverWithState statefulObserver = new ObserverWithState(observer, initialState);
        ObserverWithState previous = mObserverMap.putIfAbsent(observer, statefulObserver);
    
        if (previous != null) {
            return;
        }
        LifecycleOwner lifecycleOwner = mLifecycleOwner.get();
        if (lifecycleOwner == null) {
            // it is null we should be destroyed. Fallback quickly
            return;
        }
    
        boolean isReentrance = mAddingObserverCounter != 0 || mHandlingEvent;
        State targetState = calculateTargetState(observer);
        mAddingObserverCounter++;
        while ((statefulObserver.mState.compareTo(targetState) < 0
                && mObserverMap.contains(observer))) {
          pushParentState(statefulObserver.mState);
            statefulObserver.dispatchEvent(lifecycleOwner, upEvent(statefulObserver.mState));
          popParentState();
            // mState / subling may have been changed recalculate
            targetState = calculateTargetState(observer);
        }
    
        if (!isReentrance) {
            // we do sync only on the top level.
            sync();
        }
        mAddingObserverCounter--;
    }
    

    statefulObserver持有observer的狀態(tài)在當(dāng)前狀態(tài)不是DESTROYED的時候是INITIALIZED挨队,不存在的情況下存入mObserverMap中谷暮,這個是生命周期組件中持有的所有監(jiān)聽回調(diào)的集合,targetState是生命周期組件及其父組件中最新的狀態(tài)盛垦,while是判斷當(dāng)前的observer的狀態(tài)是否在生命周期最新狀態(tài)之前湿弦,把執(zhí)行狀態(tài)在當(dāng)前狀態(tài)之前的所有Observer都嘗試執(zhí)行dispatchEvent,并且是已添加到mObserverMap的前提下腾夯,會執(zhí)行statefulObserver.dispatchEvent方法:

    void dispatchEvent(LifecycleOwner owner, Event event) {
        State newState = getStateAfter(event);
        mState = min(mState, newState);
        mLifecycleObserver.onStateChanged(owner, event);
        mState = newState;
    }
    

    upEvent(statefulObserver.mState):

    private static Event upEvent(State state) {
        switch (state) {
            case INITIALIZED:
            case DESTROYED:
                return ON_CREATE;
            case CREATED:
                return ON_START;
            case STARTED:
                return ON_RESUME;
            case RESUMED:
                throw new IllegalArgumentException();
        }
        throw new IllegalArgumentException("Unexpected state value " + state);
    }
    

    所以這里傳入的event是ON_CREATE颊埃。

    ObserverWithState持有的是前面?zhèn)魅氲腖ifecycleBoundObserver和初始狀態(tài)INITIALIZED:

    ObserverWithState(LifecycleObserver observer, State initialState) {
        mLifecycleObserver = Lifecycling.lifecycleEventObserver(observer);
        mState = initialState;
    }
    

    對于LifecycleEventObserver類型的observer來說,Lifecycling.lifecycleEventObserver得到的就是observer本身蝶俱,所以mLifecycleObserver.onStateChanged也就是LifecycleBoundObserver的onStateChanged方法:

    @Override
    public void onStateChanged(@NonNull LifecycleOwner source,
            @NonNull Lifecycle.Event event) {
        if (mOwner.getLifecycle().getCurrentState() == DESTROYED) {
            removeObserver(mObserver);
            return;
        }
        activeStateChanged(shouldBeActive());
    }
    

    看一下activeStateChanged方法:

    void activeStateChanged(boolean newActive) {
        if (newActive == mActive) {
            return;
        }
        // immediately set active state, so we'd never dispatch anything to inactive
        // owner
        mActive = newActive;
        boolean wasInactive = LiveData.this.mActiveCount == 0;
        LiveData.this.mActiveCount += mActive ? 1 : -1;
        if (wasInactive && mActive) {
            onActive();
        }
        if (LiveData.this.mActiveCount == 0 && !mActive) {
            onInactive();
        }
        if (mActive) {
            dispatchingValue(this);
        }
    }
    

    如果mActive是true的話會執(zhí)行dispatchingValue方法班利,mActive通過shouldBeActive()賦值:

    @Override
    boolean shouldBeActive() {
        return mOwner.getLifecycle().getCurrentState().isAtLeast(STARTED);
    }
    

    可以看到,onStart之后才會觸發(fā)事件回調(diào)榨呆。注意這里dispatchingValue傳入的是this罗标,即當(dāng)前的ObserverWrapper,所以只會嘗試執(zhí)行當(dāng)前Observer的回調(diào)积蜻。

    我們可以從代碼中看到闯割,添加監(jiān)聽的時候會嘗試dispatch回調(diào),但是竿拆,我們前面的注冊監(jiān)聽放在onCreate中宙拉,那么按照邏輯,在onStart之前的并不會被執(zhí)行丙笋,但實際上為什么我們的監(jiān)聽在頁面顯示的時候自動執(zhí)行了呢谢澈?其實自動執(zhí)行是由Activity的生命周期方法觸發(fā)的,使用ReportFragment來通知不见,ComponentActivity在onCreate的時候會調(diào)用ReportFragment.injectIfNeededIn(this):

    public static void injectIfNeededIn(Activity activity) {
        if (Build.VERSION.SDK_INT >= 29) {
            // On API 29+, we can register for the correct Lifecycle callbacks directly
            activity.registerActivityLifecycleCallbacks(
                    new LifecycleCallbacks());
        }
        // Prior to API 29 and to maintain compatibility with older versions of
        // ProcessLifecycleOwner (which may not be updated when lifecycle-runtime is updated and
        // need to support activities that don't extend from FragmentActivity from support lib),
        // use a framework fragment to get the correct timing of Lifecycle events
        android.app.FragmentManager manager = activity.getFragmentManager();
        if (manager.findFragmentByTag(REPORT_FRAGMENT_TAG) == null) {
            manager.beginTransaction().add(new ReportFragment(), REPORT_FRAGMENT_TAG).commit();
            // Hopefully, we are the first to make a transaction.
            manager.executePendingTransactions();
        }
    }
    

    在sdk29以上會額外添加LifecycleCallbacks來回調(diào)澳化,為了兼容以前的版本,下面的代碼是添加一個沒有任何界面的ReportFragment稳吮,只用它來處理生命周期通知的相關(guān)工作缎谷。

    @Override
    public void onActivityCreated(Bundle savedInstanceState) {
        super.onActivityCreated(savedInstanceState);
        dispatchCreate(mProcessListener);
        dispatch(Lifecycle.Event.ON_CREATE);
    }
    
    @Override
    public void onStart() {
        super.onStart();
        dispatchStart(mProcessListener);
        dispatch(Lifecycle.Event.ON_START);
    }
    
    @Override
    public void onResume() {
        super.onResume();
        dispatchResume(mProcessListener);
        dispatch(Lifecycle.Event.ON_RESUME);
    }
    ... ...
    ... ... 
    

    可見是利用Fragment的生命周期來處理回調(diào),看一下dispatch方法:

    private void dispatch(@NonNull Lifecycle.Event event) {
        if (Build.VERSION.SDK_INT < 29) {
            // Only dispatch events from ReportFragment on API levels prior
            // to API 29. On API 29+, this is handled by the ActivityLifecycleCallbacks
            // added in ReportFragment.injectIfNeededIn
            dispatch(getActivity(), event);
        }
    }
    

    可以看到,29以下的通過ReportFragment的dispatch方法發(fā)送生命周期回調(diào)通知:

    static void dispatch(@NonNull Activity activity, @NonNull Lifecycle.Event event) {
        if (activity instanceof LifecycleRegistryOwner) {
            ((LifecycleRegistryOwner) activity).getLifecycle().handleLifecycleEvent(event);
            return;
        }
    
        if (activity instanceof LifecycleOwner) {
            Lifecycle lifecycle = ((LifecycleOwner) activity).getLifecycle();
            if (lifecycle instanceof LifecycleRegistry) {
                ((LifecycleRegistry) lifecycle).handleLifecycleEvent(event);
            }
        }
    }
    

    我們看到調(diào)用了LifecycleRegistry的handleLifecycleEvent方法列林,這個方法中又調(diào)用了moveToState方法瑞你,moveToState又調(diào)用了sync方法,在sync中會調(diào)用forwardPass或backwardPass方法希痴,這兩個方法中都會調(diào)用和前面addObserver方法中類似的代碼遍歷mObserverMap者甲,對符合執(zhí)行時機的Observer調(diào)用dispatchEvent方法。

    所以當(dāng)界面顯示的時候執(zhí)行onStart回調(diào)時就會再次dispatchEvent砌创,所以我們的監(jiān)聽回調(diào)此時就符合了“onStart之后執(zhí)行”這個觸發(fā)條件虏缸。

    那么29以上的生命周期回調(diào)怎么處理呢?上面的注釋也有提到嫩实,是根據(jù)ActivityLifecycleCallbacks來處理的刽辙。上面我們已經(jīng)看到在ComponentActivity的onCreate的時候通過ReportFragment的injectIfNeededIn方法已經(jīng)添加了一個LifecycleCallbacks:

    static class LifecycleCallbacks implements Application.ActivityLifecycleCallbacks {
        @Override
        public void onActivityCreated(@NonNull Activity activity,
                @Nullable Bundle bundle) {
        }
    
        @Override
        public void onActivityPostCreated(@NonNull Activity activity,
                @Nullable Bundle savedInstanceState) {
            dispatch(activity, Lifecycle.Event.ON_CREATE);
        }
          ... ...
    }
    

    然后在Activity的生命周期方法中都會調(diào)用相關(guān)的通知方法,以onStart來說:

    protected void onStart() {
        if (DEBUG_LIFECYCLE) Slog.v(TAG, "onStart " + this);
        mCalled = true;
    
        mFragments.doLoaderStart();
    
        dispatchActivityStarted();
    
        if (mAutoFillResetNeeded) {
            getAutofillManager().onVisibleForAutofill();
        }
    }
    

    其中調(diào)用了dispatchActivityStarted方法:

    private void dispatchActivityStarted() {
        getApplication().dispatchActivityStarted(this);
        Object[] callbacks = collectActivityLifecycleCallbacks();
        if (callbacks != null) {
            for (int i = 0; i < callbacks.length; i++) {
                ((Application.ActivityLifecycleCallbacks) callbacks[i]).onActivityStarted(this);
            }
        }
    }
    

    可以看到甲献,這里調(diào)用了每個ActivityLifecycleCallbacks的onActivityStarted方法宰缤,達到了生命周期通知回調(diào)的目的。

    不管是ReportFragment中生命周期方法內(nèi)調(diào)用還是LifecycleCallbacks調(diào)用晃洒,最后調(diào)用的都是同一個diapatch方法慨灭,都會走到LifecycleRegistry的handleLifecycleEvent中去。

  • 和組件生命周期綁定

    當(dāng)調(diào)用LiveData的observe方法時:

    @MainThread
    public void observe(@NonNull LifecycleOwner owner, @NonNull Observer<? super T> observer) {
        assertMainThread("observe");
        if (owner.getLifecycle().getCurrentState() == DESTROYED) {
            // ignore
            return;
        }
        LifecycleBoundObserver wrapper = new LifecycleBoundObserver(owner, observer);
        ObserverWrapper existing = mObservers.putIfAbsent(observer, wrapper);
        if (existing != null && !existing.isAttachedTo(owner)) {
            throw new IllegalArgumentException("Cannot add the same observer"
                    + " with different lifecycles");
        }
        if (existing != null) {
            return;
        }
        owner.getLifecycle().addObserver(wrapper);
    }
    

    LiveData里面的mObservers會保存這個observer球及,這是為了數(shù)據(jù)改變的時候會調(diào)用這個observer來通知界面組件更新氧骤。

    同時owner.getLifecycle().addObserver(wrapper)會在界面組件的LifecycleRegistry里的mObserverMap中也會保存一個observer:

    ObserverWithState statefulObserver = new ObserverWithState(observer, initialState);
    ObserverWithState previous = mObserverMap.putIfAbsent(observer, statefulObserver);
    

    而這里保存就是為了在組件銷毀的時候自動解除綁定,避免內(nèi)存泄露吃引。

    Activity通過LifecycleRegistry來通知生命周期變化的语淘,在它的setCurrentState方法流程中,拿前進時的生命周期方法forwardPass方法來說(backwardPass也一樣):

    private void forwardPass(LifecycleOwner lifecycleOwner) {
        Iterator<Entry<LifecycleObserver, ObserverWithState>> ascendingIterator =
                mObserverMap.iteratorWithAdditions();
        while (ascendingIterator.hasNext() && !mNewEventOccurred) {
            Entry<LifecycleObserver, ObserverWithState> entry = ascendingIterator.next();
            ObserverWithState observer = entry.getValue();
            while ((observer.mState.compareTo(mState) < 0 && !mNewEventOccurred
                    && mObserverMap.contains(entry.getKey()))) {
                pushParentState(observer.mState);
                observer.dispatchEvent(lifecycleOwner, upEvent(observer.mState));
                popParentState();
            }
        }
    }
    

    這里會讀取mObserverMap中所有的Observer來調(diào)用dispatchEvent方法际歼,前面提到addObserver的時候mObserverMap保存的是ObserverWithState惶翻,所以會調(diào)用ObserverWithState的dispatchEvent方法:

    void dispatchEvent(LifecycleOwner owner, Event event) {
        State newState = getStateAfter(event);
        mState = min(mState, newState);
        mLifecycleObserver.onStateChanged(owner, event);
        mState = newState;
    }
    

    mLifecycleObserver就是前面構(gòu)造ObserverWithState時傳入的observer,也就是LifecycleBoundObserver鹅心,所以會調(diào)用它的onStateChanged方法:

    @Override
    public void onStateChanged(@NonNull LifecycleOwner source,
            @NonNull Lifecycle.Event event) {
        if (mOwner.getLifecycle().getCurrentState() == DESTROYED) {
            removeObserver(mObserver);
            return;
        }
        activeStateChanged(shouldBeActive());
    }
    

    可以看到吕粗,如果當(dāng)前界面組件已經(jīng)銷毀的話,這里會從LiveData的mObserver中移除當(dāng)前的observer旭愧。

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末颅筋,一起剝皮案震驚了整個濱河市,隨后出現(xiàn)的幾起案子输枯,更是在濱河造成了極大的恐慌议泵,老刑警劉巖,帶你破解...
    沈念sama閱讀 218,525評論 6 507
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件桃熄,死亡現(xiàn)場離奇詭異先口,居然都是意外死亡,警方通過查閱死者的電腦和手機,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 93,203評論 3 395
  • 文/潘曉璐 我一進店門碉京,熙熙樓的掌柜王于貴愁眉苦臉地迎上來厢汹,“玉大人,你說我怎么就攤上這事谐宙√淘幔” “怎么了?”我有些...
    開封第一講書人閱讀 164,862評論 0 354
  • 文/不壞的土叔 我叫張陵凡蜻,是天一觀的道長搭综。 經(jīng)常有香客問我,道長划栓,這世上最難降的妖魔是什么设凹? 我笑而不...
    開封第一講書人閱讀 58,728評論 1 294
  • 正文 為了忘掉前任,我火速辦了婚禮茅姜,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘月匣。我一直安慰自己钻洒,他們只是感情好,可當(dāng)我...
    茶點故事閱讀 67,743評論 6 392
  • 文/花漫 我一把揭開白布锄开。 她就那樣靜靜地躺著素标,像睡著了一般。 火紅的嫁衣襯著肌膚如雪萍悴。 梳的紋絲不亂的頭發(fā)上头遭,一...
    開封第一講書人閱讀 51,590評論 1 305
  • 那天,我揣著相機與錄音癣诱,去河邊找鬼计维。 笑死,一個胖子當(dāng)著我的面吹牛撕予,可吹牛的內(nèi)容都是我干的鲫惶。 我是一名探鬼主播,決...
    沈念sama閱讀 40,330評論 3 418
  • 文/蒼蘭香墨 我猛地睜開眼实抡,長吁一口氣:“原來是場噩夢啊……” “哼欠母!你這毒婦竟也來了?” 一聲冷哼從身側(cè)響起吆寨,我...
    開封第一講書人閱讀 39,244評論 0 276
  • 序言:老撾萬榮一對情侶失蹤赏淌,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后啄清,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體六水,經(jīng)...
    沈念sama閱讀 45,693評論 1 314
  • 正文 獨居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 37,885評論 3 336
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發(fā)現(xiàn)自己被綠了缩擂。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片鼠冕。...
    茶點故事閱讀 40,001評論 1 348
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖胯盯,靈堂內(nèi)的尸體忽然破棺而出懈费,到底是詐尸還是另有隱情,我是刑警寧澤博脑,帶...
    沈念sama閱讀 35,723評論 5 346
  • 正文 年R本政府宣布憎乙,位于F島的核電站,受9級特大地震影響叉趣,放射性物質(zhì)發(fā)生泄漏泞边。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點故事閱讀 41,343評論 3 330
  • 文/蒙蒙 一疗杉、第九天 我趴在偏房一處隱蔽的房頂上張望阵谚。 院中可真熱鬧,春花似錦烟具、人聲如沸梢什。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,919評論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽嗡午。三九已至,卻和暖如春冀痕,著一層夾襖步出監(jiān)牢的瞬間荔睹,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 33,042評論 1 270
  • 我被黑心中介騙來泰國打工言蛇, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留僻他,地道東北人。 一個月前我還...
    沈念sama閱讀 48,191評論 3 370
  • 正文 我出身青樓腊尚,卻偏偏與公主長得像中姜,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子跟伏,可洞房花燭夜當(dāng)晚...
    茶點故事閱讀 44,955評論 2 355

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