深入理解 Jetpack 之 LiveData

mj-tangonan-1540675-unsplash.jpg
mj-tangonan-1540675-unsplash.jpg

0. 前言

本文是深入理解「Android Architecture Components」系列文章第三篇

源碼基于 android.arch.lifecycle:livedata-core:1.1.1

在之前我們深入研究了 Lifecycle 的實現(xiàn)原理疏日,并在文末提到了LiveData 以及 ViewModel禀横,這次我們來講講 LiveData而钞。

LiveData 是 Android Architecture Components 中的一員,先看下官方是如何介紹的:

LiveData is an observable data holder class. Unlike a regular observable, LiveData is lifecycle-aware, meaning it respects the lifecycle of other app components, such as activities, fragments, or services. This awareness ensures LiveData only updates app component observers that are in an active lifecycle state. [見 9.1]

This class is designed to hold individual data fields of ViewModel, but can also be used for sharing data between different modules in your application in a decoupled fashion. [見 9.2]

簡單講 LiveData 是一個能夠感知生命周期蚌本、可觀察的數(shù)據(jù)持有類 ,它被設(shè)計成 ViewModel 的一個成員變量;可以以一個 更解耦 的方式來共享數(shù)據(jù)凤壁。

實際使用下來發(fā)現(xiàn) LiveData 有幾個特性

  1. LiveData 的實現(xiàn)基于觀察者模式队寇;
  2. LiveData 跟 LifecycleOwner 綁定膘掰,能感知生命周期變化,并且只會在 LifecycleOwner 處于 Active 狀態(tài)(STARTED/RESUMED)下通知數(shù)據(jù)改變佳遣;
  3. LiveData 會自動在 DESTROYED 的狀態(tài)下移除 Observer 识埋,取消訂閱,所以不用擔(dān)心內(nèi)存泄露零渐;

那么 LiveData 上述特性的原理是怎么樣的呢窒舟?<br />使用 LiveData 又需要注意些什么呢?

本文將圍繞此展開诵盼。

<a name="d7f29183"></a>

1. LiveData 的基本使用

雖然 LiveData 通常跟 ViewModel 配合使用惠豺,不過也可以單獨使用,為了簡單起見风宁,這里不配合 ViewModel洁墙。

以下是一個簡單的例子:

MutableLiveData<String> liveString = new MutableLiveData<>();
liveString.observe(this, new Observer<String>() {
  @Override
  public void onChanged(@Nullable final String s) {
    Log.d(TAG, "onChanged() called with: s = [" + s + "]");
  }
});

liveString.postValue("程序亦非猿");

運(yùn)行后可以看到日志輸出:onChanged() called with: s = [程序亦非猿]

釋義:<br />定義一個 MutableLiveData (LiveData 的一個常用子類)戒财,通過 observe 方法可以訂閱修改數(shù)據(jù)的通知热监,通過 postValue() 或者 setValue() 方法可以更新數(shù)據(jù),已經(jīng)訂閱的 Observer 能夠得到數(shù)據(jù)更改的通知饮寞,也即回調(diào) onChanged() 方法孝扛。

這樣就算是用上 LiveData 了。

接下來幽崩,上干貨疗琉!

<a name="5f1eee0e"></a>

2. LiveData 的原理分析

在分析原理前,再明確一下我們的疑問:

  1. LiveData 是如何跟 LifecycleOwner 進(jìn)行綁定歉铝,做到感知生命周期的盈简?
  2. LiveData 只在 LifecycleOwner active 狀態(tài)發(fā)送通知,是怎么處理的?
  3. LiveData 會自動在 DESTROY 的狀態(tài)下取消訂閱柠贤,是怎么處理的香浩?
  4. 通過 setValue()/postValue() 更新數(shù)據(jù)的處理流程是如何?
  5. 生命周期變化后數(shù)據(jù)處理流程是怎么樣的臼勉?

同時提前看下我整理的 LiveData UML 圖邻吭,對 LiveData 有個整體的了解,后續(xù)的涉及到的類都在這里了宴霸,有助于理解囱晴。

image.png
image.png

<br />(圖1.LiveData 類圖)

OK, here we go!

<a name="fd594c2d"></a>

2.1 LiveData.observe()

LiveData 的使用流程從 observe() 開始瓢谢,咱們嘗試從 observe() 方法 開始分析:

    @MainThread
    public void observe(@NonNull LifecycleOwner owner, @NonNull Observer<T> observer) {
        //如果是 DESTROYED 的狀態(tài)則忽略
        if (owner.getLifecycle().getCurrentState() == DESTROYED) {
            // ignore
            return;
        }
        //把 Observer 用 LifecycleBoundObserver 包裝起來
        LifecycleBoundObserver wrapper = new LifecycleBoundObserver(owner, observer);
        //緩存起來
        ObserverWrapper existing = mObservers.putIfAbsent(observer, wrapper);
        //如果已經(jīng) observe 過 并且兩次的 owner 不同則報錯
        if (existing != null && !existing.isAttachedTo(owner)) {
            throw new IllegalArgumentException("Cannot add the same observer"
                    + " with different lifecycles");
        }
        if (existing != null) {
            return;
        }
        //綁定 owner
        owner.getLifecycle().addObserver(wrapper);
    }

可以看到 observe 方法里把我們傳遞的 observer 用 LifecycleBoundObserver 包裝了起來畸写,并且存入了 mObservers ,并且跟 owner 進(jìn)行了關(guān)聯(lián)氓扛。

并且做了兩個特殊處理:

  1. 忽視處于 DESTROYED 的 owner 的注冊行為枯芬;
  2. 將一個 Observer 同時綁定兩個 owner 的行為視為非法操作,也即一個 Observer 只能綁定一個 owner采郎,而 owner 可以有多個 Observer千所;

這里出現(xiàn)了幾個新的類 LifecycleBoundObserverObserverWrapper 來看看蒜埋。

<a name="8fee89a9"></a>

2.2 LifecycleBoundObserver

    class LifecycleBoundObserver extends ObserverWrapper implements GenericLifecycleObserver {
        @NonNull final LifecycleOwner mOwner;

        LifecycleBoundObserver(@NonNull LifecycleOwner owner, Observer<T> observer) {
            super(observer);
            mOwner = owner;
        }

        @Override
        boolean shouldBeActive() {
            // 判斷 owner 當(dāng)前的狀態(tài)是否是至少 STARTED
            return mOwner.getLifecycle().getCurrentState().isAtLeast(STARTED);
        }

        @Override
        public void onStateChanged(LifecycleOwner source, Lifecycle.Event event) {
            //生命周期改變淫痰,如果是 DESTROYED 就自動解除
            if (mOwner.getLifecycle().getCurrentState() == DESTROYED) {
                removeObserver(mObserver);
                return;
            }
            //ObserverWrapper.activeStateChanged
            activeStateChanged(shouldBeActive());
        }

        @Override
        boolean isAttachedTo(LifecycleOwner owner) {
            return mOwner == owner;
        }

        @Override
        void detachObserver() {
            mOwner.getLifecycle().removeObserver(this);
        }
    }

**ObserverWrapper **:

    private abstract class ObserverWrapper {
        final Observer<T> mObserver;
        boolean mActive;
        int mLastVersion = START_VERSION;

        ObserverWrapper(Observer<T> observer) {
            mObserver = observer;
        }
                //是否是 active 狀態(tài)
        abstract boolean shouldBeActive();

        boolean isAttachedTo(LifecycleOwner owner) {
            return false;
        }

        void detachObserver() {
        }

        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();
            }
            //如果 active 狀態(tài)下,則發(fā)送數(shù)據(jù)更新通知
            if (mActive) {
                dispatchingValue(this);
            }
        }
    }

仔細(xì)看下這兩個類其實就能解答疑問了整份。

LifecycleBoundObserver 是 抽象類 ObserverWrapper 的子類黑界,重寫了 shouldBeActive() 方法,在 owner 處于至少是 STARTED 的狀態(tài)下認(rèn)為是 active 狀態(tài)皂林;并且它也實現(xiàn)了 GenericLifecycleObserver 接口朗鸠,可以監(jiān)聽 lifecycle 回調(diào),并且在 onStateChanged() 方法里處理了生命周期改變的事件础倍,當(dāng)接收到 DESTROYED 的事件會自動解除跟 owner 的綁定烛占,并且將下個流程交給了 activeStateChanged() 。

到這里 【2.1】沟启、【2.3】的問題已經(jīng)有了答案:

【2.1】答:LifeData 在 observe 方法中用 LifecycleBoundObserver 包裝了 observer 忆家,并且通過它綁定了owner。<br />【2.3】答:LifecycleBoundObserver 在 onStateChanged() 方法里處理了生命周期改變的事件德迹,當(dāng)接收到 DESTROYED 的事件會自動解除跟 owner 的綁定芽卿。

這里需要注意的是,當(dāng)我們調(diào)用 observe() 注冊后胳搞,由于綁定了 owner卸例,所以在 active 的情況下称杨,LiveData 如果有數(shù)據(jù),則 Observer 會立馬接受到該數(shù)據(jù)修改的通知筷转。

此時的流程是:

observe--><br /> onStateChanged--><br /> activeStateChanged--><br /> dispatchingValue--><br /> considerNotify--><br /> onChanged

可以稱之為生命周期改變觸發(fā)的流程姑原,另外還有一種流程是 postValue&setValue 觸發(fā)的流程,共兩種呜舒。

<a name="73851cc0"></a>

2.3 activeStateChanged(boolean)

在 activeStateChanged() 方法里锭汛,處理了 onActive() 跟 onInactive() 回調(diào)的相關(guān)邏輯處理,并且調(diào)用了dispatchingValue(this) 袭蝗。(MediatorLiveData 用到了 onActive() 跟 onInactive() 有興趣自行了解唤殴,這里不展開)

接下去探索 dispatchingValue 。<br />

<a name="bc1af81b"></a>

2.4 dispatchingValue(ObserverWrapper) 分析

    private void dispatchingValue(@Nullable ObserverWrapper initiator) {
        //如果正在分發(fā)則直接返回
        if (mDispatchingValue) {
            //標(biāo)記分發(fā)失效
            mDispatchInvalidated = true;
            return;
        }
        //標(biāo)記分發(fā)開始
        mDispatchingValue = true;
        do {
            mDispatchInvalidated = false;
            //生命周期改變調(diào)用的方法 initiator 不為 null
            if (initiator != null) {
                considerNotify(initiator);
                initiator = null;
            } else {
                //postValue/setValue 方法調(diào)用 傳遞的 initiator 為 null
                for (Iterator<Map.Entry<Observer<T>, ObserverWrapper>> iterator =
                        mObservers.iteratorWithAdditions(); iterator.hasNext(); ) {
                    considerNotify(iterator.next().getValue());
                    if (mDispatchInvalidated) {
                        break;
                    }
                }
            }
        } while (mDispatchInvalidated);
        //標(biāo)記分發(fā)結(jié)束
        mDispatchingValue = false;
    }

considerNotify(ObserverWrapper) 方法:

    private void considerNotify(ObserverWrapper observer) {
        //檢查狀態(tài) 確保不會分發(fā)給 inactive 的 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;
        }
        //setValue 會增加 version ,初始 version 為-1
        if (observer.mLastVersion >= mVersion) {
            return;
        }
        observer.mLastVersion = mVersion;
        //noinspection unchecked
        observer.mObserver.onChanged((T) mData);
    }

可以看到 dispatchingValue 正是分發(fā)事件邏輯的處理方法到腥,而 considerNotify 方法則確保了只將最新的數(shù)據(jù)分發(fā)給 active 狀態(tài)下的 Observer 朵逝。

另外也可以看到 LiveData 引入了版本管理來管理數(shù)據(jù) (mData)以確保發(fā)送的數(shù)據(jù)總是最新的。(具體不多講)

dispatchingValue 這里分兩種情況:

  1. ObserverWrapper 不為 null
  2. ObserverWrapper 為 null

需要著重講一下左电。

<a name="7e259814"></a>

2.4.1 ObserverWrapper 不為 null 的情況

上面提到過廉侧,LifecycleBoundObserver.onStateChanged 方法里調(diào)用了 activeStateChanged 页响,而該方法調(diào)用dispatchingValue(this);傳入了 this 篓足,也就是 LifecycleBoundObserver ,這時候不為 null 闰蚕。

也就是說生命周期改變觸發(fā)的流程就是這種情況栈拖,這種情況下,只會通知跟該 Owner 綁定的 Observer没陡。

<a name="7a435db2"></a>

2.4.2 ObserverWrapper 為 null 的情況

上面我也提前說了涩哟,除了生命周期改變觸發(fā)的流程外,還有 postValue&setValue 流程盼玄,來看下這倆方法贴彼。

private final Runnable mPostValueRunnable = new Runnable() {
    @Override
    public void run() {
        Object newValue;
        synchronized (mDataLock) {
            newValue = mPendingData;
            mPendingData = NOT_SET;
        }
        //noinspection unchecked
        //調(diào)用 setValue
        setValue((T) newValue);
    }
};

protected void postValue(T value) {
    boolean postTask;
    synchronized (mDataLock) {
        postTask = mPendingData == NOT_SET;
        mPendingData = value;
    }
    if (!postTask) {
        return;
    }
    ArchTaskExecutor.getInstance().postToMainThread(mPostValueRunnable);
}

@MainThread
protected void setValue(T value) {
    //必須在主線程調(diào)用 否則會 crash
    assertMainThread("setValue");
    mVersion++;//增加版本號
    mData = value;
    //傳入了 null
    dispatchingValue(null);
}

LiveData 的 postValue 方法其實就是把操作 post 到主線程,最后調(diào)用的還是 setValue 方法埃儿,注意 setValue 必須是在主線程調(diào)用器仗。

并且可以看到** setValue 方法調(diào)用了 dispatchingValue 方法,并傳入了 null 童番,這個時候的流程則會通知 active 的mObservers**精钮。

到這里之前的剩下的所有疑問也都可以解答了。

LiveData 的兩個流程都會走到 dispatchingValue 處理分發(fā)通知邏輯剃斧,并且在分發(fā)通知前會判斷 owner 的狀態(tài)轨香,再加上 LiveData 本身內(nèi)部的版本管理,確保了只會發(fā)送最新的數(shù)據(jù)給 active 狀態(tài)下的 Observer幼东。

注意:LiveData 對同時多次修改數(shù)據(jù)做了處理臂容,如果同時多次修改科雳,只會修改為最新的數(shù)據(jù)。

<a name="a43d4d52"></a>

3. 圖解 LiveData

<a name="b932eca5"></a>

3.1 LiveData 類圖

再看一遍類圖策橘,回顧一下:<br />
image.png
image.png

<br />(圖2.LiveData 類圖)
<a name="c979bac4"></a>

3.2 LiveData 流程圖

Lifecycle 改變觸發(fā)流程:

livedata-lifecycle-changes.png
livedata-lifecycle-changes.png

<br />(圖3.Lifecycle 改變觸發(fā)流程圖)

Lifecycle postValue/setValue 觸發(fā)流程:

livedata-postvalue.png
livedata-postvalue.png

<br />(圖4.setValue 改變觸發(fā)流程圖)

<a name="6becaec8"></a>

4. LiveData tips and recipes

<br />LiveData 還有很多其他相關(guān)知識炸渡,這里列舉一些,更多實踐可以看一下【7.6】丽已。

<a name="2c5a5752"></a>

4.1 Sticky Event

LiveData 被訂閱時蚌堵,如果之前已經(jīng)更改過數(shù)據(jù),并且當(dāng)前 owner 為 active 的狀態(tài)沛婴,activeStateChanged() 會被調(diào)用吼畏,也即會立馬通知到 Observer ,這樣其實就類似 EventBus 的 sticky event 的功能嘁灯,需要注意的是泻蚊,很多時候我們并不需要該功能。具體可以看一下【7.6】的處理丑婿。

<a name="873f2dd4"></a>

4.2 AlwaysActiveObserver

默認(rèn)情況下性雄,LiveData 會跟 LicycleOwner 綁定,只在 active 狀態(tài)下更新羹奉,如若想要不管在什么狀態(tài)下都能接收到數(shù)據(jù)的更改通知的話秒旋,怎么辦?這時候需要使用 AlwaysActiveObserver 诀拭,改調(diào)用 observe 方法為調(diào)用 LiveData.observeForever(Observer) 方法即可迁筛。

<a name="4cea7239"></a>

4.3 MediatorLiveData

LiveData 還有一個子類是 MediatorLiveData,它允許我們合并多個 LiveData耕挨,任何一個 LiveData 有更新就會發(fā)送通知细卧。比如我們的數(shù)據(jù)來源有兩個,一個數(shù)據(jù)庫一個網(wǎng)絡(luò)筒占,這時候我們會有兩個 DataSource贪庙,也就是兩個 LiveData,這個時候我們可以使用 MediatorLiveData 來 merge 這兩個 LiveData翰苫。

<a name="769af84f"></a>

4.4 Transformations

Transformations 允許我們把一個 LiveData 進(jìn)行處理止邮,變化成另外一個 LiveData,目前支持 map 跟 switchMap 兩個方法革骨,跟 RxJava 的操作類似农尖。

比如,用 map 把一個 String 類型的 LiveData 轉(zhuǎn)換成 Integer 類型:

Transformations.map(liveString, new Function<String, Integer>() {
  @Override
  public Integer apply(final String input) {
    return Integer.valueOf(input);
  }
}).observe(this, new Observer<Integer>() {
  @Override
  public void onChanged(@Nullable final Integer integer) {

  }
});

<a name="661cfeb8"></a>

4.4 LiveDataBus

EventBus 基于觀察者模式良哲,LiveData 也是盛卡,所以 LiveData 可以被用來做成 LiveDataBus,有興趣可以搜索筑凫。

<a name="719fecf9"></a>

5. 知識點梳理和匯總

  1. LiveData 的實現(xiàn)基于觀察者模式(reactive patterns)滑沧;
  2. LiveData 跟 LifecycleOwner 綁定并村,能感知生命周期變化,并且只會在 LifecycleOwner 處于 Active 狀態(tài)(STARTED/RESUMED)下通知數(shù)據(jù)改變滓技;如果數(shù)據(jù)改變發(fā)生在非 active 狀態(tài)哩牍,數(shù)據(jù)會變化,但是不發(fā)送通知令漂,等 owner 回到 active 的狀態(tài)下膝昆,再發(fā)送通知;
  3. 如果想要一直收到通知叠必,則需要用 observeForever() 方法荚孵;
  4. LiveData 會自動在 DESTROYED 的狀態(tài)下移除 Observer ,取消訂閱纬朝,所以不用擔(dān)心內(nèi)存泄露收叶;
  5. 在 LifecycleOwner 處于 DESTROYED 的狀態(tài)下,不能訂閱共苛;
  6. postValue 方法其實最后調(diào)用了 setValue 只不過把操作放到主線程判没,適合在異步線程里調(diào)用,setValue 必須在主線程里調(diào)用隅茎;
  7. 如果同時多次調(diào)用 postValue 或 setValue 修改數(shù)據(jù)澄峰,只會修改成最新的那個數(shù)據(jù),也即只會收到一次通知(set post混合調(diào)用則不一定)患膛;
  8. 如果 LiveData 有數(shù)據(jù)摊阀,并且 owner 在 active 狀態(tài)下耻蛇,那么在訂閱的時候踪蹬,會立馬收到一次通知
  9. 一個 Observer 實例臣咖,只能綁定一個 LifecycleOwner跃捣,而一個 owner 可以綁定多個 Observer 實例;
  10. LiveData 利用版本管理夺蛇、綁定 Lifecycle 確保了只會發(fā)送最新的數(shù)據(jù)給 active 狀態(tài)下的 Observer疚漆;

<a name="aa5fbf5b"></a>

6. 總結(jié)

LiveData 基于觀察者模式,并且可以感知生命周期刁赦,這使得我們使用 LiveData 既可以享受觀察者模式帶來的隔離數(shù)據(jù)與 UI 等強(qiáng)大的解耦能力娶聘,還可以享受感知生命周期帶來的巨大便利。并且還無需擔(dān)心內(nèi)存泄露這個令人頭疼的問題甚脉。

我們可以使用 LiveData 非常輕松地做到一些非常高效的操作丸升,如僅在 active 的狀態(tài)下刷新 UI,可以避免不必要的數(shù)據(jù)刷新牺氨。

顯而易見 LiveData 本身的優(yōu)秀特性有著巨大的價值狡耻,利用好絕對是架構(gòu)設(shè)計中的一大利器墩剖,另外 LiveData 配合 ViewModel 可以發(fā)揮更大的價值,機(jī)智的你一定已經(jīng)知道下一篇文章講什么了夷狰。

<a name="b5b4ac7f"></a>

7. 參考與推薦

  1. LiveData Overview : https://developer.android.com/topic/libraries/architecture/livedata
  2. LiveData doc : https://developer.android.com/reference/android/arch/lifecycle/LiveData
  3. https://developer.android.com/reference/android/arch/lifecycle/LiveData.html
  4. https://developer.android.com/reference/android/arch/lifecycle/Transformations.html
  5. https://github.com/googlesamples/android-architecture-components
  6. https://github.com/googlesamples/android-sunflower
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末岭皂,一起剝皮案震驚了整個濱河市,隨后出現(xiàn)的幾起案子沼头,更是在濱河造成了極大的恐慌爷绘,老刑警劉巖,帶你破解...
    沈念sama閱讀 206,839評論 6 482
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件进倍,死亡現(xiàn)場離奇詭異揉阎,居然都是意外死亡,警方通過查閱死者的電腦和手機(jī)背捌,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 88,543評論 2 382
  • 文/潘曉璐 我一進(jìn)店門毙籽,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人毡庆,你說我怎么就攤上這事坑赡。” “怎么了么抗?”我有些...
    開封第一講書人閱讀 153,116評論 0 344
  • 文/不壞的土叔 我叫張陵毅否,是天一觀的道長。 經(jīng)常有香客問我蝇刀,道長螟加,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 55,371評論 1 279
  • 正文 為了忘掉前任吞琐,我火速辦了婚禮捆探,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘站粟。我一直安慰自己黍图,他們只是感情好,可當(dāng)我...
    茶點故事閱讀 64,384評論 5 374
  • 文/花漫 我一把揭開白布奴烙。 她就那樣靜靜地躺著助被,像睡著了一般。 火紅的嫁衣襯著肌膚如雪切诀。 梳的紋絲不亂的頭發(fā)上揩环,一...
    開封第一講書人閱讀 49,111評論 1 285
  • 那天,我揣著相機(jī)與錄音幅虑,去河邊找鬼丰滑。 笑死,一個胖子當(dāng)著我的面吹牛翘单,可吹牛的內(nèi)容都是我干的吨枉。 我是一名探鬼主播蹦渣,決...
    沈念sama閱讀 38,416評論 3 400
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼貌亭!你這毒婦竟也來了柬唯?” 一聲冷哼從身側(cè)響起,我...
    開封第一講書人閱讀 37,053評論 0 259
  • 序言:老撾萬榮一對情侶失蹤圃庭,失蹤者是張志新(化名)和其女友劉穎锄奢,沒想到半個月后,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體剧腻,經(jīng)...
    沈念sama閱讀 43,558評論 1 300
  • 正文 獨居荒郊野嶺守林人離奇死亡拘央,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 36,007評論 2 325
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發(fā)現(xiàn)自己被綠了书在。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片灰伟。...
    茶點故事閱讀 38,117評論 1 334
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖儒旬,靈堂內(nèi)的尸體忽然破棺而出栏账,到底是詐尸還是另有隱情,我是刑警寧澤栈源,帶...
    沈念sama閱讀 33,756評論 4 324
  • 正文 年R本政府宣布挡爵,位于F島的核電站,受9級特大地震影響甚垦,放射性物質(zhì)發(fā)生泄漏茶鹃。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點故事閱讀 39,324評論 3 307
  • 文/蒙蒙 一艰亮、第九天 我趴在偏房一處隱蔽的房頂上張望闭翩。 院中可真熱鬧,春花似錦垃杖、人聲如沸男杈。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,315評論 0 19
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至旺垒,卻和暖如春彩库,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背先蒋。 一陣腳步聲響...
    開封第一講書人閱讀 31,539評論 1 262
  • 我被黑心中介騙來泰國打工骇钦, 沒想到剛下飛機(jī)就差點兒被人妖公主榨干…… 1. 我叫王不留,地道東北人竞漾。 一個月前我還...
    沈念sama閱讀 45,578評論 2 355
  • 正文 我出身青樓眯搭,卻偏偏與公主長得像窥翩,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子鳞仙,可洞房花燭夜當(dāng)晚...
    茶點故事閱讀 42,877評論 2 345

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