Android mvvm 之 LiveData 的原理

目錄

  • 1. 生命周期感知
    • 1.1 生命周期感知組件
    • 1.2 LifecycleOwner 的狀態(tài)和事件模型
  • 2. LiveData 與 LifecycleOwner 的雙向訂閱
    • 2.1 LiveData 訂閱生命周期變化
    • 2.2 LifecycleOwner 訂閱數(shù)據(jù)變化
    • 2.3 多對多的雙向訂閱網(wǎng)
  • 3 LiveData 的事件變化
  • 4 LifecycleOwner 的事件變化
    • 4.1 Lifecycle 接口的實(shí)現(xiàn)——LifecycleRegistry
      • 4.1.1 LifecycleRegistry 的訂閱實(shí)現(xiàn)
      • 4.1.2 LifecycleRegistry 中的事件流
      • 4.1.3 處理生命周期的變化
  • 5. 關(guān)于觀察者模式的一點(diǎn)思考




1. 生命周期感知

1.1 生命周期感知組件

我們知道合武,Controller(Activity or Fragment) 都是有生命周期的撕阎,但是傳統(tǒng)的 Controller 實(shí)現(xiàn)方式只負(fù)責(zé) Controller 本身的生命周期管理,而與業(yè)務(wù)層的數(shù)據(jù)之間并沒有實(shí)現(xiàn)良好解耦的生命周期事件交換轮纫。所以業(yè)務(wù)層都需要自己主動去感知 Controller 生命周期的變化趁仙,并在 Controller 的生存期處理數(shù)據(jù)的焙樘恚活,而在消亡時刻解除與 Controller 之間的關(guān)系雀费,這種處理方式隨著業(yè)務(wù)規(guī)模的擴(kuò)大往往顯得代碼臃腫和難以維護(hù)薇组。

Jetpack 框架讓 Controller 變得可感知,成為一個生命周期事件變化的通知中心坐儿,我們實(shí)現(xiàn)的任何組件或?qū)ο蠖伎梢酝ㄟ^訂閱這個通知中心實(shí)時知道 Controller 生命周期的變化(這樣的組件或?qū)ο笪覀兎Q之為生命周期感知組件),而不需要繁瑣的主動詢問宋光;同時貌矿,Jetpack 推薦將數(shù)據(jù)封裝成 LiveData,因?yàn)?LiveData 自帶感知 Controller 的生命周期變化罪佳,自我維護(hù)數(shù)據(jù)更新與生命周期更新的協(xié)調(diào)關(guān)系逛漫。LiveData 是典型的生命周期感知組件。而現(xiàn)在的 Controller 我們稱之為生命周期可感知 Controller赘艳,在 Jetpack 的實(shí)現(xiàn)中酌毡,有一個專門的接口來代表它——LifecycleOwner,名副其實(shí)蕾管,Controller 是生命周期的所有者枷踏。

1.2 LifecycleOwner 的狀態(tài)和事件模型

LifecycleOwner 中維護(hù)一個叫做 Lifecycle 的接口,它規(guī)定了生命周期的狀態(tài)和狀態(tài)切換的事件流模型掰曾。在 android developer 的官方文檔中旭蠕,給出了一個非常清晰的時序圖來說明這個模型:

lifecycle_states.png
  • 生命周期的狀態(tài)總共有 5 個:DESTROYED,INITIALIZED,CREATED掏熬,STARTED佑稠,RESUMED;
  • 狀態(tài)切換事件總共有 7 個:ON_CREATE旗芬,ON_START舌胶,ON_RESUME,ON_PAUSE疮丛,ON_STOP幔嫂,ON_DESTROY,ON_ANY这刷;
  • 每個事件除了 ON_ANY 以外婉烟,都嚴(yán)格在 Controller 的 onXXX() 回調(diào)中產(chǎn)生,比如 ON_CREATE 事件在 Activity.onCreate() 和 Fragment.onCreate() 中進(jìn)行分派暇屋;
  • 需要注意 CREATED 和 STARTED 兩個狀態(tài)的入度都為 2似袁,即有兩個不同的事件都能達(dá)到這個狀態(tài)。




2. LiveData 與 LifecycleOwner 的雙向訂閱

在講述 LiveData 的原理時咐刨,沒有辦法孤立地談 LiveData昙衅,因?yàn)樗膶?shí)現(xiàn)和 LifecycleOwner 的交互是分不開的,所以這里需要將兩者結(jié)合進(jìn)行說明定鸟。

2.1 LiveData 訂閱生命周期變化

LiveData 作為 View 的 UI 狀態(tài)數(shù)據(jù)源而涉,并不是在 LifecycleOwner 的每個生命周期狀態(tài)中都可用的,而必須在 View 完成所有的測量和布局操作后联予,才能基于 LiveData 進(jìn)行 UI 狀態(tài)更新啼县。這說明 LiveData 是有一個可用狀態(tài)標(biāo)記的,在源代碼中沸久,標(biāo)記為 active:

LiveData 中更新 active 標(biāo)記的方法:

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

這說明季眷,只有當(dāng) LifecycleOwner 的狀態(tài)至少是 STARTED,LiveData 才是處于激活狀態(tài)的卷胯。再看 Lifecycle.State 的枚舉順序:

public enum State {
    DESTROYED,
    INITIALIZED,
    CREATED,
    STARTED,
    RESUMED;

    /**
     * Compares if this State is greater or equal to the given {@code state}.
     *
     * @param state State to compare with
     * @return true if this State is greater or equal to the given {@code state}
     */
    public boolean isAtLeast(@NonNull State state) {
        return compareTo(state) >= 0;
    }
}

進(jìn)一步說明子刮,只有當(dāng) LifecycleOwner 的狀態(tài)是 STARTED 和 RESUMED 時,LiveData 才是處于激活狀態(tài)的窑睁,而只有在激活狀態(tài)下挺峡,LiveData 才會將最新數(shù)據(jù)變化通知給它的訂閱者:

private void considerNotify(ObserverWrapper observer) {
    if (!observer.mActive) { // 沒有激活,不進(jìn)行通知
        return;
    }

    // 在 LifecycleOwner 的生命周期變化事件分派之前担钮,需要提前主動更新一下激活狀態(tài)橱赠,
    // 如果未激活,同樣不進(jìn)行通知
    if (!observer.shouldBeActive()) { 
        observer.activeStateChanged(false);
        return;
    }
    
    //...省略非關(guān)鍵代碼

    observer.mObserver.onChanged((T) mData);
}

嚴(yán)格的說這里并不應(yīng)該叫 LiveData 的激活狀態(tài)箫津,而應(yīng)該是向 LiveData 進(jìn)行訂閱的 LifecycleOwner 的激活狀態(tài)病线,此時 LifecycleOwner 作為觀察者觀察 LiveData 的變化吓著。所以這里可能叫 LiveData 在每一個 LifecycleOwner 上的分身的激活狀態(tài)更合適,為了表述方便送挑,我們就統(tǒng)稱叫 LiveData 的激活狀態(tài)绑莺。我們將在 2.2 節(jié)描述 LifecycleOwner 如何訂閱 LiveData。

以上惕耕,只為了說明一個問題:LiveData 需要訂閱 LifecycleOwner纺裁,感知其生命周期變化:

livedata_observe_lifecycleowner.png

圖示說明,LiveData 訂閱 LifecycleOwner司澎,而由 LifecycleOwner.Lifecycle 代理完成生命周期狀態(tài)變化通知欺缘,所以 LiveData 直接能感知的是 Lifecycle。

2.2 LifecycleOwner 訂閱數(shù)據(jù)變化

LifecycleOwner 在 STARTED 和 RESUMED 的狀態(tài)下可以根據(jù) LiveData 更新 UI 的狀態(tài)挤安,所以 LifecycleOwner 需要訂閱 LiveData 的數(shù)據(jù)變化谚殊。

在實(shí)際實(shí)現(xiàn)當(dāng)中,LifecycleOwner 作為抽象層并不具體負(fù)責(zé)訂閱 LiveData蛤铜,而是由業(yè)務(wù)層在 LifecycleOwner 中完成具體的訂閱工作嫩絮,此時我們稱 LifecycleOwner 為 Controller 更合適,雖然它們往往是同一個東西:

lifecycleowner_observe_livedata.png

注意圖示围肥,一個 User-defined Observer 必須和一個 LifecycleOwner 唯一綁定剿干,否則將無法訂閱。試想穆刻,如果一個 Observer 同時綁定兩個 LifecycleOwner:L1 和 L2置尔,假如 L1 處于 RESUMED 的狀態(tài),而 L2 處于 DESTROYED 的狀態(tài)氢伟,那么 LiveData 將無所適從:如果遵循 L1 的狀態(tài)榜轿,將變化通知給 Observer,則更新 L2 會出錯朵锣;如果遵循 L2 的狀態(tài)谬盐,不將變化通知給 Observer,則 L1 得不到及時更新猪勇。

2.3 多對多的雙向訂閱網(wǎng)

LiveData 和 LifecycleOwner 之間因?yàn)樾枰嗷ビ^察對方狀態(tài)的變化,從而需要實(shí)現(xiàn)雙向訂閱颠蕴;同時泣刹,為了支持良好的可擴(kuò)展能力,各自都維護(hù)了一個觀察者列表犀被,形成一個多對多的雙向訂閱網(wǎng)絡(luò):

bidirection_subscribes.png

我們看到一個 LiveData 是可以同時向多個 LifecycleOwner 發(fā)起訂閱的椅您,所以,LiveData 本身其實(shí)并不實(shí)際維護(hù)一個激活狀態(tài)寡键,真正的激活狀態(tài)維護(hù)在 LifecycleOwner 的 User-defined observer 中掀泳。




3 LiveData 的事件變化

LiveData 值更新之后的需要通知訂閱者(觀察者),其通知流程非常簡單:

livedata_setvalue.png

其中,判斷觀察者是否激活员舵,即判斷 LifecycleOwner 是否處于 STARTED 或 RESUMED 狀態(tài)脑沿,在 2.1 節(jié)中已有說明。

我們看一下關(guān)鍵的源代碼:

// 入口
@MainThread
protected void setValue(T value) {
    // 必須在主線程調(diào)用
    assertMainThread("setValue");

    //..省略非關(guān)鍵代碼

    // 設(shè)置新值并派發(fā)通知
    mData = value;
    dispatchingValue(null);
}

// 通知派發(fā)流程
void dispatchingValue(@Nullable ObserverWrapper initiator) {
    //..省略非關(guān)鍵代碼

    // 遍歷觀察者列表
    for (Iterator<Map.Entry<Observer<? super T>, ObserverWrapper>> iterator =
            mObservers.iteratorWithAdditions(); iterator.hasNext(); ) {
        // 嘗試通知觀察者
        considerNotify(iterator.next().getValue());

        //..省略非關(guān)鍵代碼
    }
}

其中 LiveData.considerNotify() 在 2.1 節(jié)中已有說明马僻。




4 LifecycleOwner 的事件變化

對于 LifecycleOwner 來說庄拇,其變化的事件即為生命周期狀態(tài)的變化。在 LifecycleOwner 的事件委托者 Lifecycle 看來韭邓,無論是發(fā)生了 ON_CREATE 事件還是 ON_START 事件措近,或是任何其它的事件,其事件的切換流程都是通用的女淑。

換言之瞭郑,只要 Lifecycle 接口的實(shí)現(xiàn)者實(shí)現(xiàn)這一通用切換流程,便只需給 LifecycleOwner 暴露一個切換入口鸭你,就能在 LifecycleOwner 的各個生命周期回調(diào)函數(shù)中調(diào)用這個入口就可以了屈张。這樣我們在 LifecycleOwner 中應(yīng)該可以看到形如這樣的流程(偽代碼表示):

public class Activity/Fragment implements LifecycleOwner {
    @Override
    public onCrate() {
        //...省略非關(guān)鍵代碼

        // 在 Jetpack 框架中,LifecycleImpl 被命名為 LifecycleRegistry
        LifecycleImpl.handleLifecycleEvent(ON_CREATE);
    }

    @Override
    public onStart() {
        //...省略非關(guān)鍵代碼
        LifecycleImpl.handleLifecycleEvent(ON_START);
    }

    @Override
    public onResume() {
        //...省略非關(guān)鍵代碼
        LifecycleImpl.handleLifecycleEvent(ON_RESUME);
    }

    @Override
    public onPause() {
        //...省略非關(guān)鍵代碼
        LifecycleImpl.handleLifecycleEvent(ON_PAUSE);
    }

    @Override
    public onDestroy() {
        //...省略非關(guān)鍵代碼
        LifecycleImpl.handleLifecycleEvent(ON_DESTROY);
    }
}

當(dāng)然苇本,在具體的源代碼中袜茧,與上述偽代碼會有一些出入,但是大體的結(jié)構(gòu)是一致的瓣窄。在 Jetpack 框架中笛厦,這個 Lifecycle 的實(shí)現(xiàn)者叫做 LifecycleRegistry。所以我們這里重點(diǎn)需要關(guān)注的就是 LifecycleRegistry 這個 Lifecycle 的代理接口的實(shí)現(xiàn)類是如何通知生命周期事件變化的俺夕。

4.1 Lifecycle 接口的實(shí)現(xiàn)——LifecycleRegistry

4.1.1 LifecycleRegistry 的訂閱實(shí)現(xiàn)

如 2.2 節(jié)所述裳凸,通過 LiveData.observe(owner, user-defined observer),LifecycleOwner 的業(yè)務(wù)層向 LiveData 訂閱數(shù)據(jù)變化劝贸,而在 LiveData.observe() 方法內(nèi)姨谷,同時會自動通過 Lifecycle.addObserver(LiveData-defined observer) 向 LifecycleOwner 訂閱生命周期變化:

// LiveData.observe()
@MainThread
public void observe(@NonNull LifecycleOwner owner, @NonNull Observer<? super T> observer) {

    //...省略非關(guān)鍵代碼

    LifecycleBoundObserver wrapper = new LifecycleBoundObserver(owner, observer);

    //...省略非關(guān)鍵代碼

    // 向 LifecycleOwner 發(fā)起訂閱
    owner.getLifecycle().addObserver(wrapper);
}

以上方法內(nèi)的 owner.getLifecycle() 的實(shí)際對象即為 LifecycleRegistry,我們來看一下 LifecycleRegistry.addObserver() 的基本訂閱流程:

lifecycleregistry_addobserver.png

從整個流程來看映九,總體可以分為三步:

  • 第一步是初始化觀察者對象的狀態(tài)梦湘,并將觀察者緩存入隊;
  • 第二步是以模擬派發(fā)生命周期事件的形式件甥,將新加入的觀察者的狀態(tài)提升到目前為止可提升的最大狀態(tài)捌议;
  • 第三步是同步所有觀察者的狀態(tài)到全局狀態(tài)。

我們可以看到引有,最后所有的觀察者的狀態(tài)都要同步到全局狀態(tài)瓣颅,全局狀態(tài)即為 LifecyclerOwner 最新的狀態(tài)。那么為什么需要進(jìn)行這么繁瑣的逐步模擬派發(fā)事件來進(jìn)行同步呢譬正?直接一步到位不行么宫补?

我們可以考慮一個生命周期感知組件 LifeLocation檬姥,其功能是用于進(jìn)行定位,它訂閱了 LifecycleOwner粉怕,我們假設(shè) LifeLocation 需要在 CREATED 的時候進(jìn)行一些必要的初始化健民,而在 STARTED 的時候開始執(zhí)行定位操作。假如在 LifecycleRegistry 中的狀態(tài)同步可以一步同步到全局狀態(tài)斋荞,那么有可能當(dāng)前的全局狀態(tài)已經(jīng)是 RESUMED 的了荞雏,這樣 LifeLocation 既得不到初始化,也無從啟用定位功能了平酿。

所以凤优,以上這種看似繁瑣的模擬派發(fā)狀態(tài)事件的步驟是完全必要的,它讓用戶自定義的生命周期感知組件的狀態(tài)切換流程是可預(yù)測的蜈彼。

4.1.2 LifecycleRegistry 中的事件流

我們在 4.1.1 節(jié)中的流程圖的第 6 步中提到筑辨,要根據(jù) observer.state 來計算下一個狀態(tài)事件,也就是說按照事件的流向幸逆,根據(jù)當(dāng)前的狀態(tài)棍辕,下一個要發(fā)生的事件是什么。我們修改一下 1.2 節(jié)的時序圖如下:

lifecycleregistry_event_flow.png

觀察到圖中左邊的藍(lán)色箭頭还绘,舉個例子楚昭,假如當(dāng)前的狀態(tài)是 CREATED,那么接下來要發(fā)生的事件應(yīng)該是 ON_START拍顷。藍(lán)色箭頭指示的事件流方向是生命周期由無到生的過程抚太,我們稱為 upEvent 流;與此對應(yīng)昔案,右邊的紅色箭頭指示的事件流方向是生命周期由生到死的過程尿贫,我們稱之為 downEvent。

4.1.1 節(jié)中的流程圖的第 6 步中正好需要進(jìn)行 upEvent 流操作踏揣。除此以外庆亡,我們在第 7 步同步到全局狀態(tài)時,還需要用到 upEvent 和 downEvent 流操作捞稿,且在 LifecycleOwner 的每一次生命周期的變化中又谋,都需要進(jìn)行上述第 7 步的狀態(tài)同步操作。接下來我們就看一看娱局,當(dāng) LifecycleOwner 生命周期變化后彰亥,發(fā)生了什么。

4.1.3 處理生命周期的變化

在 4 節(jié)開頭我們描述了 LifecycleImpl.handleLifecycleEvent() 方法铃辖,在 LifecycleRegistry 中也有一個同名的方法剩愧,其功能就是處理 LifecycleOwner 生命周期的變化猪叙。handleLifecycleEvent() 的處理過程是這樣的:

handlelifecycleevent.png

如圖所示:

  • Sync 標(biāo)記的部分是進(jìn)行狀態(tài)同步的核心流程娇斩,同時也是 4.1.1 節(jié)流程圖中的第 7 步的具體實(shí)現(xiàn)仁卷;
  • 每一次生命周期的變化有可能是從無到生的 up 變化,也有可能是從生到死的 down 變化犬第;
  • 如果是 up 變化锦积,則需要進(jìn)行 upEvent 流處理,如果是 down 變化歉嗓,則需要進(jìn)行 downEvent 流處理丰介;
  • 根據(jù) 4.1.1 節(jié)的描述,我們可以得出鉴分,在觀察者隊列中的所有觀察者哮幢,從最老(最開始)到最新(最末),必定維持一個不變性質(zhì):非降序排列志珍;
  • 所以當(dāng) STATE < eldestState 時橙垢,說明觀察者隊列中的所有觀察者狀態(tài)都大于全局狀態(tài),這時候說明生命周期變化順序是 down 方向的伦糯,需要進(jìn)行 downEvent 流處理柜某;
  • 而當(dāng) STATE > newestState 時,說明觀察者隊列中的所有觀察者狀態(tài)都小于全局狀態(tài)敛纲,這時候說明生命周期變化順序是 up 方向的喂击,需要進(jìn)行 upEvent 流處理;
  • 無論是 downEvent 流還是 upEvent 流淤翔,都會逐步派發(fā)生命周期事件給各個觀察者翰绊。

關(guān)于 downEvent 流和 upEvent 流,我畫了一張更加形象的圖用以加深理解:

downevent_and_upevent.png

至此办铡,整個 LiveData 和 Lifecycle 的原理就介紹完成了辞做。




5. 關(guān)于觀察者模式的一點(diǎn)思考

不難看出,LiveData 和 Lifecycle 的核心是觀察者模式寡具。無論是 LiveData 還是 Lifecycle秤茅,它們的共同點(diǎn)就是都需要維護(hù)一個穩(wěn)定的狀態(tài)機(jī):

  • LiveData 的狀態(tài)機(jī)就是數(shù)據(jù)值的變化,每個值就是一個狀態(tài)童叠,理論上可以是一個無限狀態(tài)機(jī)框喳;
  • Lifecycle 的狀態(tài)機(jī)就是生命周期的變化,每個生命周期階段就是一個狀態(tài)厦坛,它是一個有限狀態(tài)機(jī)五垮。

在涉及到狀態(tài)機(jī)模型時,如果我們需要感知狀態(tài)機(jī)當(dāng)前的狀態(tài)杜秸,一般有兩種方式:主動詢問和被動通知放仗。在復(fù)雜的業(yè)務(wù)中,主動詢問狀態(tài)機(jī)往往是不好的實(shí)踐撬碟;而被動通知诞挨,可以讓我們的業(yè)務(wù)按照狀態(tài)進(jìn)行清晰的分段莉撇,更易于模塊化和測試。觀察者模式就是一種很好的被動通知模式惶傻。

所以棍郎,當(dāng)我們的對象維護(hù)了一個狀態(tài)機(jī)的時候,可以考慮是否可以采用觀察者模式來讀取狀態(tài)银室。但是需要注意的是涂佃,觀察者模式內(nèi)部是維護(hù)了一個觀察者引用的列表的,當(dāng)狀態(tài)發(fā)生變化的時候蜈敢,是采用順序遍歷的方式逐個進(jìn)行通知的辜荠,可以想到,當(dāng)一個被觀察者中維護(hù)的觀察者數(shù)量很多抓狭,其中又有很多觀察者對狀態(tài)的響應(yīng)處理都比較耗時的話侨拦,會出現(xiàn)性能瓶頸。尤其是在基于單線程的 UI 環(huán)境下辐宾,更加需要引起注意狱从,我們通常應(yīng)該有一個機(jī)制來移除不再需要的觀察者,以減輕通知負(fù)載叠纹。






說明:

該文檔參考的 androidx 版本為

core: 1.1.0

lifecyle: 2.2.0-alpha01

fragment: 1.1.0-alpha09


如果大家喜歡我寫的文章季研,歡迎關(guān)注我的公眾號——小舍,將有更多有趣的內(nèi)容分享給大家

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末誉察,一起剝皮案震驚了整個濱河市与涡,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌持偏,老刑警劉巖驼卖,帶你破解...
    沈念sama閱讀 217,542評論 6 504
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場離奇詭異鸿秆,居然都是意外死亡酌畜,警方通過查閱死者的電腦和手機(jī),發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 92,822評論 3 394
  • 文/潘曉璐 我一進(jìn)店門卿叽,熙熙樓的掌柜王于貴愁眉苦臉地迎上來桥胞,“玉大人,你說我怎么就攤上這事考婴》废海” “怎么了?”我有些...
    開封第一講書人閱讀 163,912評論 0 354
  • 文/不壞的土叔 我叫張陵沥阱,是天一觀的道長缎罢。 經(jīng)常有香客問我,道長,這世上最難降的妖魔是什么策精? 我笑而不...
    開封第一講書人閱讀 58,449評論 1 293
  • 正文 為了忘掉前任在岂,我火速辦了婚禮,結(jié)果婚禮上蛮寂,老公的妹妹穿的比我還像新娘。我一直安慰自己易茬,他們只是感情好酬蹋,可當(dāng)我...
    茶點(diǎn)故事閱讀 67,500評論 6 392
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著抽莱,像睡著了一般范抓。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上食铐,一...
    開封第一講書人閱讀 51,370評論 1 302
  • 那天匕垫,我揣著相機(jī)與錄音,去河邊找鬼虐呻。 笑死象泵,一個胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的斟叼。 我是一名探鬼主播偶惠,決...
    沈念sama閱讀 40,193評論 3 418
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼朗涩!你這毒婦竟也來了忽孽?” 一聲冷哼從身側(cè)響起,我...
    開封第一講書人閱讀 39,074評論 0 276
  • 序言:老撾萬榮一對情侶失蹤谢床,失蹤者是張志新(化名)和其女友劉穎兄一,沒想到半個月后,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體识腿,經(jīng)...
    沈念sama閱讀 45,505評論 1 314
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡出革,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 37,722評論 3 335
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發(fā)現(xiàn)自己被綠了渡讼。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片蹋盆。...
    茶點(diǎn)故事閱讀 39,841評論 1 348
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖硝全,靈堂內(nèi)的尸體忽然破棺而出栖雾,到底是詐尸還是另有隱情,我是刑警寧澤伟众,帶...
    沈念sama閱讀 35,569評論 5 345
  • 正文 年R本政府宣布析藕,位于F島的核電站,受9級特大地震影響凳厢,放射性物質(zhì)發(fā)生泄漏账胧。R本人自食惡果不足惜竞慢,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,168評論 3 328
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望治泥。 院中可真熱鬧筹煮,春花似錦、人聲如沸居夹。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,783評論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽准脂。三九已至劫扒,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間狸膏,已是汗流浹背沟饥。 一陣腳步聲響...
    開封第一講書人閱讀 32,918評論 1 269
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留湾戳,地道東北人贤旷。 一個月前我還...
    沈念sama閱讀 47,962評論 2 370
  • 正文 我出身青樓,卻偏偏與公主長得像砾脑,于是被迫代替她去往敵國和親遮晚。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 44,781評論 2 354

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