深入理解AAC架構(gòu) - LiveData 使用以及整體機(jī)制源碼


LiveData的主要工作:

一個帶有生命周期偵測特性的數(shù)據(jù)持有者。
AAC架構(gòu)中的實(shí)際數(shù)據(jù)持有者搁骑,通常存放在ViewModel中,通過ViewModel脫離生命周期對數(shù)據(jù)的影響又固。
本身機(jī)制是觀察者模式仲器,在數(shù)據(jù)更新時,會通知所有已激活 (或根據(jù)需求通知未激活) 的觀察者最新數(shù)據(jù)仰冠。
由于生命周期偵測的特性乏冀,LiveData在添加觀察者時,要求觀察者附帶其生命周期所在的LifecycleOwner洋只。


LiveData的主要類:

  • LiveData
    核心功能類辆沦,抽象類昼捍。
    LiveData模塊的主體,模塊的基本實(shí)現(xiàn)都在該類中肢扯。

  • MutableLiveData
    核心功能類妒茬。
    LiveData的非抽象子類,只是把postValue()setValue()的訪問權(quán)限擴(kuò)大成public并可實(shí)例化蔚晨。

  • Observer
    核心功能類郊闯,觀察者接口。

  • LiveData$ObserverWrapper
    LiveData的內(nèi)部類蛛株,觀察者實(shí)例的容器類的抽象父類团赁。
    注冊到LiveData的觀察者,被會ObserverWrapper的子類包裝谨履,再加入觀察者列表欢摄。
    維護(hù)了對應(yīng)觀察者的:
    1- 當(dāng)前數(shù)據(jù)版本
    2- 當(dāng)前觀察者的激活狀態(tài)
    同時負(fù)責(zé)通知LiveDataonActive()onInactive()

  • LiveData$AlwaysActiveObserver
    ObserverWrapper的一個子類笋粟。
    通過LiveData.observeForever()傳入的觀察者將會被此類包裝怀挠。
    被該類包裝的觀察者不關(guān)注生命周期,不受生命周期影響害捕,并且激活狀態(tài)永久為true绿淋。
    使用該方法時應(yīng)該注意手動釋放觀察者,以避免內(nèi)存泄漏尝盼。

  • LiveData$LifecycleBoundObserver
    核心功能類吞滞,ObserverWrapper的一個子類。
    通過LiveData.observe()傳入的觀察者將會被此類包裝盾沫。
    該類是LiveData生命周期監(jiān)管的核心裁赠,該類實(shí)現(xiàn)了LifecycleEventObserver接口,并在創(chuàng)建后注冊到觀察者對應(yīng)的生命周期內(nèi)赴精,以實(shí)現(xiàn)激活狀態(tài)管理和主動釋放佩捞。

  • MediatorLiveData
    MutableLiveData的一個子類。
    該類除了LiveData的功能外蕾哟,允許對多個LiveData的進(jìn)行觀察一忱。
    簡單的描述:
    MutableLiveData會把需要觀察的LiveData和對應(yīng)的Observer封裝為一個Source,自身則維護(hù)著Source列表谭确。
    當(dāng)MutableLiveData激活時帘营,遍歷Source列表,以AlwaysActiveObserver的形式對需要觀察的LiveData進(jìn)行注冊琼富。
    MutableLiveData失去激活時仪吧,則自動移除Source列表中所有LiveData注冊。
    任意LiveData數(shù)據(jù)更新時鞠眉,根據(jù)需求薯鼠,決定是否對自身進(jìn)行維護(hù)或更新,即實(shí)現(xiàn)了對任意個LiveData的數(shù)據(jù)更新事件進(jìn)行管理械蹋。

  • Transformations
    工具類出皇,不可實(shí)例化。
    用于實(shí)現(xiàn)LiveData的數(shù)據(jù)進(jìn)行一定的流處理操作哗戈,例如發(fā)布控制郊艘,類型轉(zhuǎn)換等。
    工具類的方法唯咬,實(shí)際上是通過MediatorLiveData對傳入的LiveData進(jìn)行觀察纱注,當(dāng)LiveData更新數(shù)據(jù)時,數(shù)據(jù)會傳入的用戶定義的方法中進(jìn)行控制或轉(zhuǎn)換胆胰,然后選擇性通過MediatorLiveData.setValue()發(fā)布處理后的數(shù)據(jù)狞贱。
    工具提供了3個方法:(livedata-2.2.0)
    1- map
    使LiveData<X>通過Function轉(zhuǎn)換為LiveData<Y>
    即:LiveData<X>數(shù)據(jù)更新 - > Function轉(zhuǎn)換 -> MediatorLiveData<Y>數(shù)據(jù)更新
    ..
    2- switchMap
    根據(jù)LiveData<X>的數(shù)據(jù)蜀涨,根據(jù)Function返回需要觀察的LiveData<Y>瞎嬉。
    即:LiveData<X>數(shù)據(jù)更新 - > Function選擇數(shù)據(jù)源 -> MediatorLiveData<Y>數(shù)據(jù)源更新。
    ..
    map對比厚柳,map的數(shù)據(jù)源直接來自LiveData<X>氧枣;
    switchMap的數(shù)據(jù)源是Function返回的LiveData<Y>LiveData<X>對于switchMap來說只是個數(shù)據(jù)源控制器别垮。
    ..
    3- distinctUntilChanged
    屏蔽LiveData<X>的重復(fù)數(shù)據(jù)發(fā)射便监。
    即:LiveData<X>setValue被調(diào)用后,將會檢測數(shù)據(jù)是否變更 ( 通過equals() ) 碳想,未發(fā)生變更時茬贵,將會屏蔽本次更新。

  • ComputableLiveData
    一個未公開的類移袍。(livedata-2.2.0)
    并非LiveData的子類解藻,而是內(nèi)部維護(hù)了一個LiveData
    該類內(nèi)部LiveData的數(shù)據(jù)來源葡盗,并不來自數(shù)據(jù)的設(shè)置螟左,而是來自子類實(shí)現(xiàn)的compute()方法的返回值。當(dāng)數(shù)據(jù)更新時觅够,調(diào)用invalidate()方法通知LiveData重新調(diào)用compute()計(jì)算數(shù)據(jù)胶背。默認(rèn)運(yùn)行在一個大小為4,專用IO操作的線程池(arch_disk_io_%i)喘先。


LiveData的基本使用

  • 一般使用:
class VM : ViewModel() {
    val dataSource = MutableLiveData<String>()
}

fun simple(vmOwner: ViewModelStoreOwner, lcOwner: LifecycleOwner) {
    val vm = ViewModelProvider(vmOwner).get(VM::class.java)
    vm.dataSource.observe(lcOwner, Observer { })
}
  • 合并觀察:
class VM : ViewModel() {
    val intSource = MutableLiveData<Int>()
    val dataSource = MutableLiveData<String>()
}

fun simple(vmOwner: ViewModelStoreOwner, lcOwner: LifecycleOwner) {
    val vm = ViewModelProvider(vmOwner).get(VM::class.java)
    mediator.addSource(vm.intSource) { mediator.value = it.toString() }
    mediator.addSource(vm.dataSource) { mediator.value = it }
}
  • 類型裝換:
fun simple() {
    val source: LiveData<Int> = MutableLiveData<Int>()
    val transfor: LiveData<String> = Transformations.map(source) {
        it.toString()
    }
}
  • 數(shù)據(jù)源控制
class VMCtrl : ViewModel() {
    private val stateSource = MutableLiveData<Int>()
    private val dataSource1 = MutableLiveData<String>()
    private val dataSource2 = MutableLiveData<String>()
    
    // stateSource 數(shù)據(jù)變更時會導(dǎo)致 outputSource 的數(shù)據(jù)源變更
    // 從而使 outputSource 數(shù)據(jù)發(fā)生變更
    val outputSource = Transformations.switchMap(stateSource) {
        if (it > 0) {
            dataSource1
        } else {
            dataSource2
        }
    }
}
  • 重復(fù)事件屏蔽
class VMUntilChanged : ViewModel() {
    private val source = MutableLiveData<Int>()
    // source 設(shè)置同一數(shù)值钳吟,無論多少次,outputSource 只會觸發(fā)一次數(shù)據(jù)更新
    val outputSource = Transformations.distinctUntilChanged(stateSource)
}

LiveData的關(guān)鍵源碼分析

  • LiveData的源碼并不復(fù)雜窘拯,先從構(gòu)造方法進(jìn)行分析:

在構(gòu)造方法中就可以直接看到LiveData的兩個關(guān)鍵成員
1- mData:當(dāng)前LiveData保存的數(shù)據(jù)红且。
2- mVersion:當(dāng)前mData的版本坝茎,默認(rèn)-1,通過設(shè)置數(shù)據(jù)的構(gòu)造方法實(shí)例化時暇番,版本為0嗤放。

static final Object NOT_SET = new Object();
static final int START_VERSION = -1;

public LiveData() {
    mData = NOT_SET;
    mVersion = START_VERSION;
}

public LiveData(T value) {
    mData = value;
    mVersion = START_VERSION + 1;
}

  • LiveData.observe()作為入口分析:
  1. mObservers
    LiveData的觀察者列表,可以看到實(shí)際對象是ObserverWrapper類型壁酬,即所有傳入的observer都會被ObserverWrapper及其子類封裝次酌。
  2. assertMainThread()
    該處將會檢測是否在主線程調(diào)用,否則拋出異常舆乔。
  3. LifecycleBoundObserver
    ObserverWrapper子類岳服,觀察者的生命周期管理在此處實(shí)現(xiàn)。
  4. existing.isAttachedTo(owner)
    觀察者不允許綁定多個生命周期希俩,否則拋出異常吊宋。
    同一個LiveData中,觀察者不能同時 持有 / 忽略 生命周期特性斜纪,兩者互斥贫母。
  5. addObserver(wrapper)
    LifecycleBoundObserver加入對應(yīng)生命周期回調(diào)以實(shí)現(xiàn)自動生命周期管理
private SafeIterableMap<Observer<? super T>, ObserverWrapper> mObservers = new SafeIterableMap<>();

public void observe(@NonNull LifecycleOwner owner, @NonNull Observer<? super T> observer) {
    assertMainThread("observe");
    if (owner.getLifecycle().getCurrentState() == DESTROYED) {
        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);
}

  • 在分析LifecycleBoundObserver前,先對其父類ObserverWrapper進(jìn)行分析:
  1. mObserver
    在構(gòu)造方法中傳入的觀察者實(shí)體盒刚,即用戶構(gòu)建的觀察者腺劣。
  2. mActive
    當(dāng)前觀察者的激活狀態(tài),非激活狀態(tài)時將不會觸發(fā)觀察者的回調(diào)因块。
  3. mLastVersion
    當(dāng)前觀察者對應(yīng)的數(shù)據(jù)版本橘原。
  4. shouldBeActive()
    判斷觀察者是否需要激活,由子類實(shí)現(xiàn)涡上。
  5. isAttachedTo()
    判斷觀察者是否綁定了生命周期趾断,由子類實(shí)現(xiàn),默認(rèn)為false吩愧。
  6. detachObserver()
    觀察者從LiveData移除時芋酌,LiveData會調(diào)用觀察者包裝對象的該方法,以處理必要的釋放操作雁佳。
  7. activeStateChanged()
    觀察者的激活狀態(tài)通過該方法作出改變脐帝。
private abstract class ObserverWrapper {
    final Observer<? super T> mObserver;
    boolean mActive;
    int mLastVersion = START_VERSION;

    ObserverWrapper(Observer<? super T> observer) {
        mObserver = observer;
    }

    abstract boolean shouldBeActive();

    boolean isAttachedTo(LifecycleOwner owner) {
        return false;
    }

    void detachObserver() {
    }

    void activeStateChanged(boolean newActive) {
        if (newActive == mActive) {
            return;
        }

        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);
        }
    }
}
  • 其中第7點(diǎn)activeStateChanged()在狀態(tài)改變時,有如下性質(zhì):
  1. LiveData沒有任何已激活的觀察者糖权,且此觀察者激活堵腹,會回調(diào)LiveDataonActive()方法
  2. 此觀察者取消激活后星澳,LiveData沒有任何已激活的觀察者疚顷,會回調(diào)LiveDataonInactive()方法
  3. 觀察者激活時,會通過dispatchingValue()通知LiveData嘗試派發(fā)數(shù)據(jù)到觀察者腿堤。

  • 關(guān)鍵方法dispatchingValue()

該方法實(shí)際上LiveDatamObservers列表遍歷進(jìn)行通知嘗試的方法阀坏。
當(dāng)指定觀察者時,嘗試對該觀察者派發(fā)數(shù)據(jù)释液;當(dāng)未指定具體的觀察者時全释,將會遍歷整個mObservers列表装处,對所有觀察者派發(fā)數(shù)據(jù)误债。

  • dispatchingValue()的具體的流程:

當(dāng)前正在執(zhí)行派發(fā)時,標(biāo)記當(dāng)次派發(fā)失效妄迁,在while循環(huán)處重新派發(fā)寝蹈;
指定派發(fā)目標(biāo)時,只對該目標(biāo)進(jìn)行派發(fā)登淘;
未指定派發(fā)目標(biāo)時箫老,遍歷mObservers,對所有觀察者進(jìn)行派發(fā)黔州,派發(fā)期間檢查失效標(biāo)記耍鬓,失效時,跳出當(dāng)次遍歷流妻,在while循環(huán)處重新派發(fā)牲蜀。

  1. mDispatchingValue:用于標(biāo)記是否真正派發(fā)數(shù)據(jù)。
  2. mDispatchInvalidated:用于標(biāo)記當(dāng)前派發(fā)是否失效绅这。
  3. considerNotify():觀察者在該方法中根據(jù)條件判斷是否進(jìn)行派分?jǐn)?shù)據(jù)涣达。
private boolean mDispatchingValue;
private boolean mDispatchInvalidated;

void dispatchingValue(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;
}

  • 實(shí)際的派發(fā)方法considerNotify()

該方法比較簡單,主要是就是對觀察者進(jìn)行激活和版本校驗(yàn)证薇。
通過校驗(yàn)后度苔,記錄最新版本,并推送最新數(shù)據(jù)到觀察者浑度。

  1. !observer.mActive:當(dāng)觀察者未激活時寇窑,不執(zhí)行派發(fā)。
  2. observer.shouldBeActive():重復(fù)檢查觀察者是否應(yīng)該處于激活狀態(tài)箩张,檢查失敗則通知觀察者取消激活甩骏。
  3. observer.mLastVersion >= mVersion:對比觀察者版本,僅在版本低于LiveData版本時伏钠,通知觀察者更新横漏。
private void considerNotify(ObserverWrapper observer) {
    if (!observer.mActive) {
        return;
    }

    if (!observer.shouldBeActive()) {
        observer.activeStateChanged(false);
        return;
    }
    if (observer.mLastVersion >= mVersion) {
        return;
    }
    observer.mLastVersion = mVersion;
    observer.mObserver.onChanged((T) mData);
}
  • 以上即可知道整個派發(fā)流程,即通過dispatchingValue()發(fā)起熟掂,considerNotify()校驗(yàn)缎浇,最終派發(fā)到觀察者。

  • 回頭分析LifecycleBoundObserver

前文提到赴肚,LifecycleBoundObserverLiveData.observe()時素跺,即綁定到觀察者所指定的生命周期中二蓝,而綁定通過owner.getLifecycle().addObserver()傳入自身作為對象,即LifecycleBoundObserver必然實(shí)現(xiàn)LifecycleObserver指厌。

  1. mOwner:在構(gòu)造方法中要求傳入生命周期對象刊愚。
  2. shouldBeActive():當(dāng)處于生命周期處于Start - Stop之間時,觀察者激活踩验。
  3. onStateChanged():生命周期回調(diào)鸥诽。
    非Destory狀態(tài),通過activeStateChanged(shouldBeActive())進(jìn)行自我狀態(tài)校驗(yàn)和處理箕憾。
    當(dāng)處于Destory時牡借,通知LiveData移除觀察者,LiveData會把包裝類從mObservers列表中移除袭异,且觸發(fā)其detachObserver()方法钠龙。
  4. isAttachedTo():直接判斷生命周期對象是否相同。
  5. detachObserver():將自身從生命周期中移除御铃,完成整個解綁過程碴里。
class LifecycleBoundObserver 
          extends ObserverWrapper 
          implements LifecycleEventObserver {

    final LifecycleOwner mOwner;

    LifecycleBoundObserver(LifecycleOwner owner, Observer observer) {
        super(observer);
        mOwner = owner;
    }

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

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

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

    @Override
    void detachObserver() {
        mOwner.getLifecycle().removeObserver(this);
    }
}
  • 通過LifecycleBoundObserver,即LiveData.observe()加入的觀察者上真,實(shí)現(xiàn)了生命周期偵測咬腋,完成激活狀態(tài)管理和自動解綁操作。

  • 數(shù)據(jù)更新:

數(shù)據(jù)更新有2個方法谷羞,且都比較簡單帝火,這里不貼出源碼。

  1. setValue()
    只能在主線程執(zhí)行湃缎,非主線程調(diào)用拋出異常犀填。
    mVersion+1,mData設(shè)置為傳入數(shù)據(jù)嗓违,并通過dispatchingValue(null)通知所有觀察者數(shù)據(jù)數(shù)據(jù)更新九巡。
    注意數(shù)據(jù)設(shè)置時,不會進(jìn)行任何的對比和校驗(yàn)蹂季。即設(shè)置相同數(shù)據(jù)時冕广,同樣視為更新數(shù)據(jù)〕ソ啵·

  2. postValue()
    在任意線程中調(diào)用撒汉,傳入的數(shù)據(jù)會暫存為mPendingData,最終會通過在主線程中調(diào)用setValue(mPendingData)進(jìn)行數(shù)據(jù)更新涕滋。
    注意的是睬辐,postValue()被多次調(diào)用時,暫存數(shù)據(jù)mPendingData會被postValue()傳入的數(shù)據(jù)覆蓋,最終數(shù)據(jù)為最后一次的數(shù)據(jù)溯饵。而postValue()發(fā)起的主線程任務(wù)侵俗,在執(zhí)行到前,只會存在一個任務(wù)丰刊。即:
    多次postValue()調(diào)用隘谣,在真正主線程執(zhí)行前,共享同一次任務(wù)啄巧。而其中的暫存數(shù)據(jù)mPendingData會被覆蓋為最后一次postValue()的傳入值寻歧。
    使用postValue()應(yīng)該注意該問題,極短時間內(nèi)多次postValue()設(shè)置數(shù)據(jù)棵帽,會可能導(dǎo)致數(shù)據(jù)更新丟失熄求。


  • LiveData.observeForever()

添加忽略生命周期偵測特性的觀察者渣玲。
通過該方法加入的觀察者不需要傳入生命周期參數(shù)逗概,即忽略生命周期偵測。
所以使用該方法時需要注意爽醋,在合適的時候調(diào)用removeObserver()移除觀察调榄,避免發(fā)生內(nèi)存泄漏氛濒。

  1. 通過observeForever()傳入的觀察者,將會被包裝為AlwaysActiveObserver铅搓,該包裝類的shouldBeActive()方法估計(jì)返回true,即任何數(shù)據(jù)更新都會通知到該觀察者搀捷。
  2. 同一個LiveData中星掰,觀察者不能同時 持有 / 忽略 生命周期特性,兩者互斥嫩舟。
  3. 新加入的觀察者氢烘,在方法的最后都會通過activeStateChanged(true)通知LiveData更新數(shù)據(jù)到觀察者。
public void observeForever(Observer<? super T> observer) {
    assertMainThread("observeForever");
    AlwaysActiveObserver wrapper = new AlwaysActiveObserver(observer);
    ObserverWrapper existing = mObservers.putIfAbsent(observer, wrapper);
    if (existing instanceof LiveData.LifecycleBoundObserver) {
        throw new IllegalArgumentException("Cannot add the same observer"
                    + " with different lifecycles");
    }
    if (existing != null) {
        return;
    }
    wrapper.activeStateChanged(true);
}

  • 觀察者移除:

觀察者移除有2個方法家厌,且都比較簡單播玖,這里不貼出源碼。

  1. removeObserver(Observer observer)
    該方法用于移除指定的觀察者饭于,如果綁定生命周期蜀踏,則會解除綁定。
  2. removeObservers(LifecycleOwner owner)
    僅針對通過LiveData.observe()添加掰吕,綁定了生命周期的觀察者果覆,移除維護(hù)中的所有生命周期對象為傳入對象的觀察者。最終也是通過removeObserver()逐一移除殖熟。
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末局待,一起剝皮案震驚了整個濱河市,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌燎猛,老刑警劉巖恋捆,帶你破解...
    沈念sama閱讀 206,968評論 6 482
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場離奇詭異重绷,居然都是意外死亡沸停,警方通過查閱死者的電腦和手機(jī),發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 88,601評論 2 382
  • 文/潘曉璐 我一進(jìn)店門昭卓,熙熙樓的掌柜王于貴愁眉苦臉地迎上來愤钾,“玉大人,你說我怎么就攤上這事候醒∧馨洌” “怎么了?”我有些...
    開封第一講書人閱讀 153,220評論 0 344
  • 文/不壞的土叔 我叫張陵倒淫,是天一觀的道長伙菊。 經(jīng)常有香客問我,道長敌土,這世上最難降的妖魔是什么镜硕? 我笑而不...
    開封第一講書人閱讀 55,416評論 1 279
  • 正文 為了忘掉前任,我火速辦了婚禮返干,結(jié)果婚禮上兴枯,老公的妹妹穿的比我還像新娘。我一直安慰自己矩欠,他們只是感情好财剖,可當(dāng)我...
    茶點(diǎn)故事閱讀 64,425評論 5 374
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著癌淮,像睡著了一般躺坟。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上该默,一...
    開封第一講書人閱讀 49,144評論 1 285
  • 那天瞳氓,我揣著相機(jī)與錄音,去河邊找鬼栓袖。 笑死匣摘,一個胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的裹刮。 我是一名探鬼主播音榜,決...
    沈念sama閱讀 38,432評論 3 401
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼捧弃!你這毒婦竟也來了赠叼?” 一聲冷哼從身側(cè)響起擦囊,我...
    開封第一講書人閱讀 37,088評論 0 261
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎嘴办,沒想到半個月后瞬场,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 43,586評論 1 300
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡涧郊,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 36,028評論 2 325
  • 正文 我和宋清朗相戀三年贯被,在試婚紗的時候發(fā)現(xiàn)自己被綠了。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片妆艘。...
    茶點(diǎn)故事閱讀 38,137評論 1 334
  • 序言:一個原本活蹦亂跳的男人離奇死亡彤灶,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出批旺,到底是詐尸還是另有隱情幌陕,我是刑警寧澤,帶...
    沈念sama閱讀 33,783評論 4 324
  • 正文 年R本政府宣布汽煮,位于F島的核電站搏熄,受9級特大地震影響,放射性物質(zhì)發(fā)生泄漏逗物。R本人自食惡果不足惜搬卒,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 39,343評論 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望翎卓。 院中可真熱鬧,春花似錦摆寄、人聲如沸失暴。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,333評論 0 19
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽逗扒。三九已至,卻和暖如春欠橘,著一層夾襖步出監(jiān)牢的瞬間矩肩,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 31,559評論 1 262
  • 我被黑心中介騙來泰國打工肃续, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留黍檩,地道東北人。 一個月前我還...
    沈念sama閱讀 45,595評論 2 355
  • 正文 我出身青樓始锚,卻偏偏與公主長得像刽酱,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子瞧捌,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 42,901評論 2 345

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