Android單向數(shù)據(jù)流——MvRx核心源碼解析

前言

背景知識(shí)旭斥,Android真響應(yīng)式架構(gòu)——MvRx彩掐。

MvRx是什么铭若?最簡單的解釋是搪搏,Mv(ModelView)和Rx(ReactiveX)狭握,MvRx是AAC ModelView和RxJava的結(jié)合,但這只是表象疯溺,MvRx最核心的價(jià)值在于论颅,它是React理念在原生Android開發(fā)領(lǐng)域的一種實(shí)現(xiàn),并且這種實(shí)現(xiàn)充分利用了AAC和Kotlin的能力囱嫩,以一種相對簡單的方式實(shí)現(xiàn)恃疯,降低了它的理解和使用門檻,對客戶端是比較友好的墨闲。那什么是React的理念呢今妄?我認(rèn)為關(guān)鍵有兩點(diǎn):

  1. View由狀態(tài)(State)表征
  2. 狀態(tài)由事件(Event,或者稱為動(dòng)作鸳碧、意圖盾鳞,不同地方有不同的叫法)單向驅(qū)動(dòng)

如上圖所示,使用State數(shù)據(jù)渲染View瞻离,View產(chǎn)生Event事件腾仅,發(fā)送給StateStore,StateStore根據(jù)舊的State構(gòu)造出新的State套利,再傳遞給View推励。這種單向數(shù)據(jù)流的架構(gòu)在其它地方也被稱作MVI(Model-View-Intent)架構(gòu)。

那么實(shí)現(xiàn)這么一套架構(gòu)復(fù)雜嗎肉迫?其實(shí)還是很復(fù)雜的验辞,其中涉及到State的管理,對State的觀察昂拂,View如何由State表征等等一系列問題受神。但是,如果借助現(xiàn)有的一些庫格侯,整個(gè)實(shí)現(xiàn)也并不復(fù)雜鼻听。State用Kotlin data class來表示,從View到StateStore的連接借助ViewModel來實(shí)現(xiàn)联四,State的管理StateStore使用RxJava中的Subject來完成撑碴,對State的觀察使用LifecycleObserver+RxJava Observer來實(shí)現(xiàn),從State到View的渲染借助Epoxy來完成朝墩。這就是MvRx的設(shè)計(jì)思路醉拓,所以MvRx本身的源碼并不多,也不復(fù)雜。本文主要對MvRx的核心源碼StateStore和State Observer進(jìn)行解析亿卤。源碼版本com.airbnb.android:mvrx:1.5.1愤兵。

1. 太長不看

每個(gè)MvRxViewModel都包含一個(gè)MvRxStateStore,通過MvRxViewModel對MvRxStateStore進(jìn)行的setState,getState的操作會(huì)被分別放進(jìn)兩個(gè)隊(duì)列中排吴,并且會(huì)給MvRxStateStore中的一個(gè)BehaviorSubject發(fā)送一個(gè)信號秆乳,BehaviorSubject收到信號后,會(huì)在一個(gè)單獨(dú)的線程從setState,getState的隊(duì)列中取元素钻哩,先從setState隊(duì)列中取屹堰,之后是getState隊(duì)列(也就是說異步getState獲取的State是之前還沒執(zhí)行的setState執(zhí)行之后的最新的State),直至getState隊(duì)列都為空街氢;每次從setState隊(duì)列取一個(gè)元素(元素類型State.()->State扯键,一般稱之為reducer)后,都會(huì)執(zhí)行這個(gè)reducer珊肃,完成從舊State到新State的更新荣刑,然后通知StateObservable,這樣外部就能觀察到State的變化了近范。

上圖出自Unidirectional data flow on Android using Kotlin嘶摊,他把Event稱為Action,這篇文章從概念上解釋了什么是Android單向數(shù)據(jù)流评矩,如果你不關(guān)心具體的源碼實(shí)現(xiàn)叶堆,只是想從概念上對單向數(shù)據(jù)流建立一個(gè)圖像,那么推薦你看看這篇文章斥杜。

2. MvRxStateStore

如上所述虱颗,StateStore的職責(zé)就是對State的管理,主要包括接收外部輸入蔗喂,由舊生新忘渔;讓外部可以觀察State的變化,基本上就這些缰儿。MvRx定義了一個(gè)接口MvRxStateStore明確了這些職責(zé):

interface MvRxStateStore<S : Any> : Disposable {
    val state: S //同步方式獲取 state
    fun get(block: (S) -> Unit) //異步方式獲取 state
    fun set(stateReducer: S.() -> S) // 更新 state畦粮,由舊生新
    val observable: Observable<S> //外部觀察 state
}

獲取State的方式有兩種:同步和異步,獲取State并不等同于觀察State乖阵,一般是為了根據(jù)當(dāng)前State決定下一步的動(dòng)作宣赔,是一種一次性的行為。

每個(gè)MvRxViewModel都必須明確它的State瞪浸,并且每個(gè)MvRxViewModel也都包含一個(gè)MvRxStateStore:

interface MvRxState

abstract class BaseMvRxViewModel<S : MvRxState>(
    initialState: S,
    debugMode: Boolean,
    private val stateStore: MvRxStateStore<S> = RealMvRxStateStore(initialState)
) : ViewModel() {
    //通過stateStore同步獲取state
    internal val state: S
        get() = stateStore.state
    
    
    protected fun setState(reducer: S.() -> S) {
        if (debugMode) {
            //debug模式下會(huì)進(jìn)行一些驗(yàn)證
            //核心邏輯就是儒将,reducer運(yùn)行兩遍,兩遍運(yùn)行得到的 state要求相等对蒲,這基本上保證了reducer是個(gè)“純函數(shù)”
        } else {
            stateStore.set(reducer)
        }
    }

    
    protected fun withState(block: (state: S) -> Unit) {
        stateStore.get(block)
    }
}

MvRxState只是一個(gè)標(biāo)記接口钩蚊,所以State可以是任意類贡翘,只要它實(shí)現(xiàn)了MvRxState接口。但是砰逻,BaseMvRxViewModel包含了對State的驗(yàn)證邏輯鸣驱,要求State必須是Kotlin data class,并且其所有屬性必須是不可變的(immutable)蝠咆,不能在data class中定義var屬性丐巫,并且不能使用諸如MutableListMutableMap之類的可變集合勺美,這是為了方便MvRxStateStore對State的管理,強(qiáng)制要求改變State必須通過MvRxStateStore由舊生新碑韵。
每個(gè)MvRxViewModel都包含一個(gè)MvRxStateStore赡茸,默認(rèn)值是RealMvRxStateStore,并且構(gòu)造時(shí)就傳入了初始State祝闻,初始State對于RealMvRxStateStore而言是很重要的占卧。
BaseMvRxViewModelMvRxStateStore的使用是很直接的,其實(shí)對MvRxStateStore.observable也是很直接的使用联喘,但是observable的觀察者比較復(fù)雜华蜒,我們之后再看。

3. RealMvRxStateStore

class RealMvRxStateStore<S : Any>(initialState: S) : MvRxStateStore<S> {
    //State Subject
    private val subject: BehaviorSubject<S> = BehaviorSubject.createDefault(initialState)

    //刷新“隊(duì)列”的信號
    private val flushQueueSubject = BehaviorSubject.create<Unit>()

    //“隊(duì)列”實(shí)體豁遭,Jobs類的定義之后會(huì)看到
    private val jobs = Jobs<S>()

    //State Observable
    override val observable: Observable<S> = subject
    //同步獲取 state
    override val state: S
        get() = subject.value!! //必然不為null叭喜,因?yàn)閟ubject創(chuàng)建的時(shí)候提供了初始值 initialState

    init {
        //在一個(gè)單獨(dú)的線程刷新隊(duì)列,避免setState的競爭
        flushQueueSubject.observeOn(Schedulers.newThread())
            //在flushQueueSubject收到信號的時(shí)候蓖谢,刷新隊(duì)列捂蕴,state由舊生新的核心邏輯就在flushQueues方法中
            .subscribe({ flushQueues() }, ::handleError)
            .registerDisposable()
    }

    //異步獲取 state
    override fun get(block: (S) -> Unit) {
        //入getState隊(duì)列,然后發(fā)送信號
        jobs.enqueueGetStateBlock(block)
        flushQueueSubject.onNext(Unit)
    }

    override fun set(stateReducer: S.() -> S) {
        //入setState隊(duì)列闪幽,然后發(fā)送信號
        jobs.enqueueSetStateBlock(stateReducer)
        flushQueueSubject.onNext(Unit)
    }
}

一個(gè)小的背景知識(shí)啥辨,Subject是RxJava中一種既是Observable又是Observer的類,而BehaviorSubjectSubject幾個(gè)子類中最“正扯㈦纾”的那一個(gè)溉知,把它自己接收到的數(shù)據(jù),再發(fā)送出去腕够。RealMvRxStateStore中包含兩個(gè)BehaviorSubject级乍,一個(gè)就是我們的State Subject,另一個(gè)是信號Subject燕少。當(dāng)set,get State的時(shí)候卡者,會(huì)把要執(zhí)行的內(nèi)容入隊(duì)列,然后向信號Subject發(fā)送信號客们,在RealMvRxStateStore的構(gòu)造函數(shù)中就已經(jīng)注冊了信號Subject的觀察者——flushQueues()崇决,信號會(huì)在一個(gè)新的線程中被接收到材诽,接收到信號就會(huì)在這個(gè)新線程中執(zhí)行flushQueues(),雖然我們還沒有看flushQueues()的具體內(nèi)容恒傻,但想想也知道肯定就是從隊(duì)列中取內(nèi)容然后執(zhí)行脸侥,如果是setState會(huì)設(shè)置“新State”給State Subject,這樣新的state就傳遞了出去盈厘,Done睁枕。

來看看“隊(duì)列”的實(shí)體類Jobs:

class Jobs<S> {

    private val getStateQueue = LinkedList<(state: S) -> Unit>()
    private var setStateQueue = LinkedList<S.() -> S>()

    @Synchronized
    fun enqueueGetStateBlock(block: (state: S) -> Unit) {
        getStateQueue.add(block)
    }

    @Synchronized
    fun enqueueSetStateBlock(block: S.() -> S) {
        setStateQueue.add(block)
    }

    @Synchronized
    fun dequeueGetStateBlock(): ((state: S) -> Unit)? {
        //getStateQueue為空,就會(huì)返回 null
        return getStateQueue.poll()
    }

    //出隊(duì)所有setStateQueue
    @Synchronized
    fun dequeueAllSetStateBlocks(): List<(S.() -> S)>? {
        if (setStateQueue.isEmpty()) return null

        val queue = setStateQueue
        setStateQueue = LinkedList()
        return queue
    }
}

相當(dāng)之無聊沸手,就兩個(gè)LinkedList外遇,進(jìn)進(jìn)隊(duì)出出隊(duì)。RealMvRxStateStoresetget方法一般都會(huì)是在后臺(tái)線程中執(zhí)行契吉,對setStateQueue,getStateQueue進(jìn)出隊(duì)是會(huì)有多線程同步的問題跳仿,所以這些方法都加了鎖。

好了捐晶,現(xiàn)在一切都準(zhǔn)備好了菲语,就剩下flushQueues()的具體實(shí)現(xiàn)了:

class RealMvRxStateStore<S : Any>(initialState: S) : MvRxStateStore<S> {
    //不常用的 tailrec功能,即尾遞歸優(yōu)化惑灵,會(huì)把遞歸調(diào)用改為循環(huán)的模式
    private tailrec fun flushQueues() {
        //先出隊(duì) setStateQueue并且執(zhí)行
        flushSetStateQueue()
        //然后再出隊(duì) getStateQueue
        val block = jobs.dequeueGetStateBlock() ?: return
        block(state)
        //遞歸調(diào)用山上,直至getStateQueue為空
        flushQueues()
    }

    private fun flushSetStateQueue() {
        //所有setStateQueue全部出隊(duì),挨個(gè)執(zhí)行
        val blocks = jobs.dequeueAllSetStateBlocks() ?: return
        for (block in blocks) {
            val newState = state.block()
            // state不同才發(fā)送
            if (newState != state) {
                subject.onNext(newState)
            }
        }
    }
}

正如我們預(yù)期的那樣英支,flushQueues()核心邏輯就是出隊(duì)然后執(zhí)行佩憾,只不過執(zhí)行順序是有講究的。在MvRxModelView中干花,我們經(jīng)常這么寫:

getState { state ->
  if (state.isLoading) return
  //或者是發(fā)送一個(gè)網(wǎng)絡(luò)請求鸯屿,總之最后會(huì)調(diào)用 setState
  setState { state ->
    state.copy(...)
  }
}

也就是說,getStateQueue隊(duì)列中的內(nèi)容時(shí)常包含著入隊(duì)setStateQueue把敢,那么考慮下面一個(gè)問題:

getStateA {
  setStateA {}
}
getStateB {
  setStateB {}
}

如果是像上面這樣的調(diào)用順序寄摆,那么我們期望的執(zhí)行順序是getStateA->setStateA->getStateB->setStateB,你仔細(xì)思考一下flushQueues()的出隊(duì)順序修赞,得到的執(zhí)行順序正是我們期望的那樣婶恼。
最后,調(diào)用subject.onNext(newState)柏副,通過RealMvRxStateStoreoverride val observable: Observable<S> = subject勾邦,新state狀態(tài)就被傳遞了出去。注意割择,flushQueues()是在一個(gè)單獨(dú)的新線程中執(zhí)行眷篇,每個(gè)RealMvRxStateStore都會(huì)新建一個(gè)線程,多個(gè)RealMvRxStateStore之間不存在競爭荔泳。

這里補(bǔ)充一個(gè)小細(xì)節(jié)蕉饼,flushSetStateQueue方法中會(huì)依次遍歷setStateQueue出隊(duì)的各個(gè)元素虐杯,并且對每個(gè)元素執(zhí)行完reducer操作之后,都會(huì)把得到的新的State傳遞出去subject.onNext(newState)昧港,其實(shí)還有另外一種選擇擎椰,那就是“狀態(tài)折疊”,假設(shè)setStateQueue出隊(duì)元素有兩個(gè)A,B创肥,我們可以A,B依次執(zhí)行达舒,但是只對外傳遞最終的newStateB,A作為中間狀態(tài)就不對外傳遞了叹侄,這樣可以提高效率巩搏,但是這樣會(huì)引發(fā)一個(gè)問題,oldState->newStateA->newStateB趾代,如果只傳遞最終的狀態(tài)newStateB塔猾,newStateA就會(huì)消失了,至于這是否是我們想要的結(jié)果稽坤,這取決于實(shí)際情況,所以“狀態(tài)折疊”會(huì)提高效率糯俗,但是可能會(huì)有問題尿褪,不進(jìn)行“狀態(tài)折疊”會(huì)些許降低效率,但是總是一個(gè)不會(huì)出錯(cuò)方案得湘,MvRx也是從最初的“狀態(tài)折疊”調(diào)整為現(xiàn)在的不再折疊杖玲。

3. MvRxLifecycleAwareObserver

經(jīng)過RealMvRxStateStore的一番操作,新的State通過Observable被傳遞了出去淘正,要想觀察State的變化就需要一個(gè)Observer摆马,同時(shí),這個(gè)Observe還需要感知生命周期鸿吆,所以這個(gè)這個(gè)觀察者應(yīng)該是RxJava Observer + LifecycleObserver囤采,在MvRx中的實(shí)現(xiàn)就是MvRxLifecycleAwareObserver
有一點(diǎn)需要明確惩淳,無論我們以何種方式觀察State的變化,觀察整個(gè)State,還是觀察State中的某幾個(gè)屬性磺送,在View中觀察歉摧,還是在ViewModel中觀察,最終都會(huì)調(diào)用BaseMvRxViewModelsubscribeLifecycle方法:

abstract class BaseMvRxViewModel<S : MvRxState> {
    
    private fun <T : Any> Observable<T>.subscribeLifecycle(
        lifecycleOwner: LifecycleOwner? = null,
        deliveryMode: DeliveryMode,
        subscriber: (T) -> Unit
    ): Disposable {
        //觀察者邏輯在主線程執(zhí)行
        return observeOn(AndroidSchedulers.mainThread())
            .resolveSubscription(lifecycleOwner, deliveryMode, subscriber)
            .disposeOnClear()
    }

    private fun <T : Any> Observable<T>.resolveSubscription(
        lifecycleOwner: LifecycleOwner? = null,
        deliveryMode: DeliveryMode, //忽略這個(gè)參數(shù)
        subscriber: (T) -> Unit
    ): Disposable = if (lifecycleOwner == null || FORCE_DISABLE_LIFECYCLE_AWARE_OBSERVER) {
        //沒有提供生命周期激蹲,或者調(diào)試狀態(tài)棉磨,直接觀察
        this.subscribe(subscriber)
    } else {
        //綁定生命周期,那么觀察者會(huì)被包裝成 MvRxLifecycleAwareObserver
        this.subscribeWith(
            MvRxLifecycleAwareObserver(
                lifecycleOwner,
                onNext = Consumer { value ->
                    subscriber(value)
                }
            )
        )
    }
}

如果提供的觀察者subscriber綁定了生命周期学辱,那么它會(huì)被包裝成MvRxLifecycleAwareObserver乘瓤。既然我們提供了生命周期环形,那么就是想實(shí)現(xiàn)類似LiveData那樣的數(shù)據(jù)觀察模式,生命周期≥STARTED時(shí)馅扣,才通知觀察者斟赚。MvRxLifecycleAwareObserver就是實(shí)現(xiàn)這樣的邏輯。

//實(shí)現(xiàn)了三個(gè)接口 LifecycleObserver, Observer<T>, Disposable差油,我們忽略Disposable的相關(guān)邏輯拗军,主要看LifecycleObserver, Observer
internal class MvRxLifecycleAwareObserver<T : Any>(
    private var owner: LifecycleOwner?,
    private val activeState: Lifecycle.State = State.STARTED,
    private val deliveryMode: DeliveryMode = RedeliverOnStart,
    private var lastDeliveredValueFromPriorObserver: T?,
    private var sourceObserver: Observer<T>?,
    private val onDispose: () -> Unit
) : AtomicReference<Disposable>(), LifecycleObserver, Observer<T>, Disposable {
    
    //次構(gòu)造函數(shù),就是把sourceObserver一個(gè)參數(shù)拆分成onNext, onError, onComplete, onSubscribe四個(gè)參數(shù)
    constructor(
        owner: LifecycleOwner,
        activeState: Lifecycle.State = DEFAULT_ACTIVE_STATE,
        deliveryMode: DeliveryMode = RedeliverOnStart,
        lastDeliveredValue: T? = null,
        onComplete: Action = Functions.EMPTY_ACTION,
        onSubscribe: Consumer<in Disposable> = Functions.emptyConsumer(),
        onError: Consumer<in Throwable> = Functions.ON_ERROR_MISSING,
        onNext: Consumer<T> = Functions.emptyConsumer(),
        onDispose: () -> Unit
    ) : this(owner, activeState, deliveryMode, lastDeliveredValue, LambdaObserver<T>(onNext, onError, onComplete, onSubscribe), onDispose)

    //上次未傳輸?shù)闹敌罾磦鬏斒且驗(yàn)?Lifecycle未活躍
    private var lastUndeliveredValue: T? = null
    //上次的值发侵,可能已經(jīng)傳輸過了
    private var lastValue: T? = null
    //Lifecycle處于未活躍狀態(tài)時(shí)則上鎖
    private val locked = AtomicBoolean(true)
    private val isUnlocked
        get() = !locked.get()

    override fun onSubscribe(d: Disposable) {
        if (DisposableHelper.setOnce(this, d)) {
            //開始觀察時(shí),也會(huì)對生命周期進(jìn)行觀察
            owner!!.lifecycle.addObserver(this)
            sourceObserver!!.onSubscribe(this)
        }
    }

    //觀察 Lifecycle ON_DESTROY事件
    @OnLifecycleEvent(Lifecycle.Event.ON_DESTROY)
    fun onDestroy() {
        owner!!.lifecycle.removeObserver(this)
        if (!isDisposed) {
            dispose()
        }
        owner = null
        sourceObserver = null
    }

    //觀察 Lifecycle任意事件
    @OnLifecycleEvent(Event.ON_ANY)
    fun onLifecycleEvent() {
        updateLock()
    }

    private fun updateLock() {
        //每個(gè)Lifecycle事件都更新鎖妆偏,處于活躍狀態(tài)時(shí)解鎖刃鳄,否則加鎖
        if (owner?.lifecycle?.currentState?.isAtLeast(activeState) == true) {
            unlock()
        } else {
            lock()
        }
    }

    override fun onNext(nextValue: T) {
        if (isUnlocked) {
            sourceObserver!!.onNext(nextValue)
        } else {
            //記錄下未傳輸?shù)闹?            lastUndeliveredValue = nextValue
        }
        lastValue = nextValue
    }

    override fun onError(e: Throwable) {
        if (!isDisposed) {
            lazySet(DisposableHelper.DISPOSED)
            sourceObserver!!.onError(e)
        }
    }

    override fun onComplete() {
        sourceObserver!!.onComplete()
    }

    private fun unlock() {
        if (!locked.getAndSet(false)) {
            return
        }
        if (!isDisposed) {
            val valueToDeliverOnUnlock = //根據(jù) deliveryMode確定要傳輸?shù)闹?            lastUndeliveredValue = null
            //只有Lifecycle處于活躍狀態(tài),并且沒被dispose钱骂,才會(huì)傳輸值
            if (valueToDeliverOnUnlock != null) {
                onNext(valueToDeliverOnUnlock)
            }
        }
    }

    private fun lock() {
        locked.set(true)
    }
}

MvRxLifecycleAwareObserver既是RxJava Observer又是LifecycleObserver叔锐,每個(gè)Lifecycle的Event事件,都會(huì)導(dǎo)致MvRxLifecycleAwareObserver鎖標(biāo)記的更新见秽,Lifecycle處于活躍時(shí)locked = false愉烙,否則locked = true,只有在Lifecycle處于活躍時(shí)才會(huì)調(diào)用我們定義的觀察邏輯sourceObserver解取,否則只是把值記錄下來步责,待到活躍時(shí)再傳輸。最終sourceObserver接收到的值還和deliveryMode的取值有關(guān)禀苦,這是個(gè)sealed class蔓肯,只有兩種類型:RedeliverOnStartUniqueOnly,顧名思義振乏,在RedeliverOnStart模式下蔗包,值會(huì)被重新傳輸給我們的觀察者sourceObserver,哪怕這個(gè)值之前已經(jīng)傳輸過慧邮;而UniqueOnly模式下則只會(huì)傳輸還未傳輸?shù)闹灯摇1热缡謾C(jī)不斷的息屏亮屏,假設(shè)這期間State的值沒有變化赋咽,那么在RedeliverOnStart模式下旧噪,每次亮屏?xí)r,sourceObserver都會(huì)接收到之前的值脓匿;UniqueOnly模式下則不會(huì)接收到任何值(所以UniqueOnly模式一般適用于一次性的行為淘钟,例如Toast,Snackbar,Log等)。簡明起見陪毡,關(guān)于deliveryMode的邏輯米母,在上述代碼中都被刪除了勾扭。

4. 總結(jié)

MvRx最核心的邏輯就是MvRxStateStore了,這是實(shí)現(xiàn)單向數(shù)據(jù)流的關(guān)鍵铁瞒,MvRx利用了RxJava Subject的特性妙色,非常簡明地完成了StateStore的功能。State管理的核心邏輯其實(shí)就是一個(gè)多向輸入慧耍,單向輸出的過程身辨,而Subject的功能正契合這一需求,這使得RxJava Subject成了Android平臺(tái)實(shí)現(xiàn)單向數(shù)據(jù)流的不二之選——直到Kotlin coroutines的出現(xiàn)芍碧。

關(guān)于Android單向數(shù)據(jù)流/MvRx更多的思考請看Android單向數(shù)據(jù)流——Side Effect煌珊。

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個(gè)濱河市泌豆,隨后出現(xiàn)的幾起案子定庵,更是在濱河造成了極大的恐慌,老刑警劉巖踪危,帶你破解...
    沈念sama閱讀 218,036評論 6 506
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件蔬浙,死亡現(xiàn)場離奇詭異,居然都是意外死亡贞远,警方通過查閱死者的電腦和手機(jī)畴博,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 93,046評論 3 395
  • 文/潘曉璐 我一進(jìn)店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來兴革,“玉大人,你說我怎么就攤上這事蜜唾≡忧” “怎么了?”我有些...
    開封第一講書人閱讀 164,411評論 0 354
  • 文/不壞的土叔 我叫張陵袁余,是天一觀的道長擎勘。 經(jīng)常有香客問我,道長颖榜,這世上最難降的妖魔是什么棚饵? 我笑而不...
    開封第一講書人閱讀 58,622評論 1 293
  • 正文 為了忘掉前任,我火速辦了婚禮掩完,結(jié)果婚禮上噪漾,老公的妹妹穿的比我還像新娘。我一直安慰自己且蓬,他們只是感情好欣硼,可當(dāng)我...
    茶點(diǎn)故事閱讀 67,661評論 6 392
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著恶阴,像睡著了一般诈胜。 火紅的嫁衣襯著肌膚如雪豹障。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 51,521評論 1 304
  • 那天焦匈,我揣著相機(jī)與錄音血公,去河邊找鬼。 笑死缓熟,一個(gè)胖子當(dāng)著我的面吹牛累魔,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播荚虚,決...
    沈念sama閱讀 40,288評論 3 418
  • 文/蒼蘭香墨 我猛地睜開眼薛夜,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了版述?” 一聲冷哼從身側(cè)響起梯澜,我...
    開封第一講書人閱讀 39,200評論 0 276
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎渴析,沒想到半個(gè)月后晚伙,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 45,644評論 1 314
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡俭茧,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 37,837評論 3 336
  • 正文 我和宋清朗相戀三年咆疗,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片母债。...
    茶點(diǎn)故事閱讀 39,953評論 1 348
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡午磁,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出毡们,到底是詐尸還是另有隱情迅皇,我是刑警寧澤,帶...
    沈念sama閱讀 35,673評論 5 346
  • 正文 年R本政府宣布衙熔,位于F島的核電站登颓,受9級特大地震影響,放射性物質(zhì)發(fā)生泄漏红氯。R本人自食惡果不足惜框咙,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,281評論 3 329
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望痢甘。 院中可真熱鬧喇嘱,春花似錦、人聲如沸塞栅。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,889評論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至王暗,卻和暖如春悔据,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背俗壹。 一陣腳步聲響...
    開封第一講書人閱讀 33,011評論 1 269
  • 我被黑心中介騙來泰國打工科汗, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留,地道東北人绷雏。 一個(gè)月前我還...
    沈念sama閱讀 48,119評論 3 370
  • 正文 我出身青樓头滔,卻偏偏與公主長得像,于是被迫代替她去往敵國和親涎显。 傳聞我的和親對象是個(gè)殘疾皇子坤检,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 44,901評論 2 355