RxJava2 源碼解析——流程

RxJava——目前最熱門的響應式函數(shù)編程框架。
筆者也是初涉Rx,所以打算通過這篇文章來理解Rx的操作流程,加深自己對Rx的理解。
本文不涉及RxJava的入門使用,如有需有:
關于RxJava的入門推薦:拋物線大佬的精品——給 Android 開發(fā)者的 RxJava 詳解

[筆者仍為Android初學者。如有解釋錯誤的地方,歡迎評論區(qū)指正探討]

本文主要根據(jù)RxJava2的源碼解析整個流程。


引入

首先簡單的看一下關于RxJava的一般使用:

前提:定義了一個login接口,返回值為 { isSuccess, UserInfo}

Observable.create(new ObservableOnSubscribe<LoginApiResult>() {
            @Override
            public void subscribe(ObservableEmitter<LoginApiResult> e) throws Exception {
                e.onNext(login());
            }
        }) //調(diào)用登錄接口
        .map(new Function<LoginApiBean, UserInfoBean>() {
            @Override
            protected UserInfoBean decode(LoginApiBean loginApiBean) {
                //處理登錄結(jié)果,返回UserInfo
                if (loginApiBean.isSuccess()) {
                    return loginApiBean.getUserInfoBean();
                } else {
                    throw new RequestFailException("獲取網(wǎng)絡請求失敗");
                }
            }
        })
        .doOnNext(new Consumer<UserInfoBean>() {    //保存登錄結(jié)果UserInfo
            @Override
            public void accept(@NonNull UserInfoBean bean) throws Exception {
                saveUserInfo(bean);
            }
        })
        .subscribeOn(Schedulers.io())   //調(diào)度線程
        .observeOn(AndroidSchedulers.mainThread())  //調(diào)度線程
        .subscribe(new Consumer<UserInfoBean>() {
            @Override
            public void accept(@NonNull UserInfoBean bean) throws Exception {LoginApiBean
                //整個請求成功,根據(jù)獲取的UserInfo更新對應的View
                showSuccessView(bean);
            }
        }, new Consumer<Throwable>() {
            @Override
            public void accept(@NonNull Throwable throwable) throws Exception {
                //請求失敗,顯示對應的View
                showFailView();
            }
        });

為了便于理解,上述邏輯沒有對應的進行封裝,簡單的展示 RxJava 的幾個重要流程。

按著代碼的順序我們理一下步驟:

  1. 首先是通過create方法,生成一個Observable對象,并傳入一個ObservableOnSubscribe對象,在其回調(diào)方法中調(diào)用login接口返回LoginApiResult,并執(zhí)行onNext

  2. 然后通過map方法將LoginApiResult轉(zhuǎn)換成UserInfoBean

  3. 緊接著是通過doOnNext方法進行保存saveUserInfo操作

  4. 然后是線程的調(diào)度,分別通過subscribeOnobserveOn將上面提到的步驟都執(zhí)行在IO線程,下面的步驟都執(zhí)行在主(UI)線程中

  5. 最后是通過Consumer根據(jù)執(zhí)行結(jié)果完成(成功或拋異常),執(zhí)行對應的UI更新方法。

按著順序,我們一步一步的跟進看看RxJava到底是如何實現(xiàn)這些操作的。


任務鏈的構(gòu)建

入口類——Observable

既然要了解RxJava,那么比不可少的我們應該先來看看他的入口類,也就是Observable

public abstract class Observable<T> implements ObservableSource<T> {
    @Override  //交由子類實現(xiàn)的出現(xiàn)方法
    protected abstract void subscribeActual(Observer observer) ;

    @Override  //實現(xiàn)了ObservableSource的方法
    public final void subscribe(Observer<? super T> observer) {
        //省略一堆判空等處理 
        subscribeActual(observer);
    }

省略了一堆靜態(tài)方法之后,我們可以看到,Observable是一個抽象類,實現(xiàn)了ObservableSource接口,并留了subscribeActual這個抽象方法。
ObservableSource接口只定義了subscribe一個方法,可以看到這個方法做了一些基礎判斷之后直接跳轉(zhuǎn)到子類的subscribeActual方法。

所以一個被觀察者被subscribe的邏輯其實是交由Observable子類來實現(xiàn)的,每個不同的被觀察者可以根據(jù)自己的需求實現(xiàn) "被訂閱" 后的操作
(賊拗口- -md,總覺得這里用subscribe這個命名很奇怪,還是setSubscriber好懂)
(換而言之,每個子類可以實現(xiàn)各自被setSubscriber后的動作)

Create

接下來是如何生成一個Obserable對象,我們看到create方法。
create方法便是Obserable其中一個關鍵的靜態(tài)方法。
我們跟進看一下源碼:

public static <T> Observable<T> create(ObservableOnSubscribe<T> source) {
    ObjectHelper.requireNonNull(source, "source is null");
    return RxJavaPlugins.onAssembly(new ObservableCreate<T>(source));
}

首先第一句代碼是對傳入的對象進行判空,內(nèi)部內(nèi)部實現(xiàn)是如果傳入null,會拋異常。
接著是生成一個ObservableCreate對象,然后將這個對象傳入RxJavaPlugins進行組裝。
RxJavaPlugins提供了一系列的Hook function,通過鉤子函數(shù)這種方法對RxJava的標準操作進行加工,當我們沒有進行配置時,默認是直接返回原來的對象,也就是返回ObservableCreate對象。
(為了方便講解,后續(xù)將直接忽視判空和RxJavaPlugins的代碼)

分析后可以看到,這里其實直接返回一個ObservableCreate對象。
我們跟進去看一下這個對象的一些基本信息:

public final class ObservableCreate<T> extends Observable<T> {
    final ObservableOnSubscribe<T> source;

    public ObservableCreate(ObservableOnSubscribe<T> source) {
        this.source = source;
    }

可以簡單的看到,這個類繼承了Observalbe類,并存儲了我們剛才傳進去的ObservableOnSubscribe對象。當然這個類也實現(xiàn)了剛才說的subscribeActual方法,我們待會再看。

map

往下,我們調(diào)用了Obserable的map方法:
我們跟進:

public final <R> Observable<R> map(Function<? super T, ? extends R> mapper) {
    return new ObservableMap<T, R>(this, mapper);
}

可以看到其實是返回了一個ObservableMap對象,接受了兩個參數(shù),一個是this,在這里指的也就是剛才的ObservableCreate ,還有一個Function對象,
我們再跟進去看一下ObservableMap的基礎信息:

public final class ObservableMap<T, U> extends AbstractObservableWithUpstream<T, U> {
    final Function<? super T, ? extends U> function;

    public ObservableMap(ObservableSource<T> source, Function<? super T, ? extends U> function) {
        super(source);
        this.function = function;
    }

可以看到其實構(gòu)造方法和剛才的ObservableCreate一樣,將傳入的對象進行了存儲。

不過可以發(fā)現(xiàn)- -這個類并不是繼承自Observable,而是AbstractObservableWithUpstream,我們再跟進看看:

// Base class for operators with a source consumable.
// 帶有source的operator的基類
abstract class AbstractObservableWithUpstream<T, U> extends Observable<U> implements HasUpstreamObservableSource<T> {

    /** The source consumable Observable. */
    protected final ObservableSource<T> source;

    AbstractObservableWithUpstream(ObservableSource<T> source) {
        this.source = source;
    }

    @Override
    public final ObservableSource<T> source() {
        return source;
    }
}

可以看到這個父類其實繼承了Observable,看到官方的注釋可以知道,這個類是所有接受上一級輸入的操作符(operator 如map)的基類,這里的邏輯并復雜,其實只是簡單的封裝了一下上一級的輸入source和輸出先下一級的數(shù)據(jù)。

分析之后可以看到,調(diào)用了map方法其實也是返回了一個Observable對象。

doOnNext

接著往下是doOnNext,- -看到這里可以猜測也是簡單的返回一個Observable對象吧。。
不管怎么說,先進入源碼看一看:

public final Observable<T> doOnNext(Consumer<? super T> onNext) {
        return doOnEach(onNext, Functions.emptyConsumer(), Functions.EMPTY_ACTION, Functions.EMPTY_ACTION);
    }

private Observable<T> doOnEach(Consumer<? super T> onNext, Consumer<? super Throwable> onError, Action onComplete, Action onAfterTerminate) {
        return new ObservableDoOnEach<T>(this, onNext, onError, onComplete, onAfterTerminate);
    }

可以看到跳轉(zhuǎn)到doOnEach方法,傳入的參數(shù)除了我們傳進來的Consumer之外,其實都是傳了空實現(xiàn)的Consumer對象。
可以看到- -真的是簡單的返回一個Observable對象。
老規(guī)矩,先看一下ObservableDoOnEach的基礎信息:

public final class ObservableDoOnEach<T> extends AbstractObservableWithUpstream<T, T> {
    final Consumer<? super T> onNext;
    final Consumer<? super Throwable> onError;
    final Action onComplete;
    final Action onAfterTerminate;

    public ObservableDoOnEach(ObservableSource<T> source, Consumer<? super T> onNext,
                              Consumer<? super Throwable> onError,
                              Action onComplete,
                              Action onAfterTerminate) {
        super(source);
        this.onNext = onNext;
        this.onError = onError;
        this.onComplete = onComplete;
        this.onAfterTerminate = onAfterTerminate;
    }

同樣的對所有信息進行了保存。可以看到這個類也是繼承了AbstractObservableWithUpstream,可以接受上一層的輸入,并向下一層輸出數(shù)據(jù)。

subscribeOn & observeOn

= =接著是線程調(diào)度,其實不看也猜得出。。。這里也是直接返回對應的Observable對象。

首先看一下subscribeOn:

public final Observable<T> subscribeOn(Scheduler scheduler) {
     return new ObservableSubscribeOn<T>(this, scheduler);
}

再看一下ObserveOn:

public final Observable<T> observeOn(Scheduler scheduler) {
     return observeOn(scheduler, false, bufferSize());
}
 
public final Observable<T> observeOn(Scheduler scheduler, boolean delayError, int bufferSize) {
      return new ObservableObserveOn<T>(this, scheduler, delayError, bufferSize);
    }

可以看到,這里分別返回了0ObservableSubscribeOnObservableObserveOn對象。照舊我們先看看這兩個個類的基礎信息:

public final class ObservableSubscribeOn<T> extends AbstractObservableWithUpstream<T, T> {
    final Scheduler scheduler;

    public ObservableSubscribeOn(ObservableSource<T> source, Scheduler scheduler) {
        super(source);
        this.scheduler = scheduler;
    }

怎么樣- -一路看到這里,也能知道他這里的基礎信息是什么了吧。

再看看另外一個:

public final class ObservableObserveOn<T> extends AbstractObservableWithUpstream<T, T> {
    final Scheduler scheduler;
    final boolean delayError;
    final int bufferSize;
    public ObservableObserveOn(ObservableSource<T> source, Scheduler scheduler, boolean delayError, int bufferSize) {
        super(source);
        this.scheduler = scheduler;
        this.delayError = delayError;
        this.bufferSize = bufferSize;
    }

同樣的保存了傳進去的基礎信息,我們發(fā)現(xiàn)其中共有的都保存了Scheduler對象,我們先稍微看一下Scheduler

public abstract class Scheduler {

    @NonNull
    public abstract Worker createWorker();

    @NonNull
    public Disposable scheduleDirect(@NonNull Runnable run) {
        return scheduleDirect(run, 0L, TimeUnit.NANOSECONDS);
    }

    public Disposable scheduleDirect(@NonNull Runnable run, long delay, @NonNull TimeUnit unit) {
        final Worker w = createWorker();
        w.schedule(new Runnable() {
            @Override
            public void run() {
                try {
                    run.run();
                } finally {
                    w.dispose();
                }
            }
        }, delay, unit);

        return w;
    }

    public abstract static class Worker implements Disposable {

        @NonNull
        public Disposable schedule(@NonNull Runnable run) {
            return schedule(run, 0L, TimeUnit.NANOSECONDS);
        }

        @NonNull
        public abstract Disposable schedule(@NonNull Runnable run, long delay, @NonNull TimeUnit unit);

}

可以看到,Scheduler對外暴露了scheduleDirect方法,這個方法通過調(diào)用抽象方法createWorker得到worker對象,然后調(diào)用worker對象的schedule方法,執(zhí)行runnable

看到這里大致就能猜出Scheduler對應的邏輯啦,內(nèi)部的worker對象維護自己的線程池,然后每次執(zhí)行schedule方法時把runnable對象提交到線程池中。先這樣理解,最后我們再深入一下。

subscribe

終于來到最后這個方法了- -md。。。前面全都是直接返回對象,難道所有邏輯都在最后實現(xiàn)嗎?- -進去看一下先。

public final Disposable subscribe(Consumer<? super T> onNext, Consumer<? super Throwable> onError, Action onComplete, Consumer<? super Disposable> onSubscribe) {
    LambdaObserver<T> ls = new LambdaObserver<T>(onNext, onError, onComplete, onSubscribe);

    subscribe(ls);

    return ls;
}

public final void subscribe(Observer<? super T> observer) {
    subscribeActual(observer);

因為subscribe的重載方法很多- -這里只挑最終的兩個,其中LambdaObserver其實就是把傳進來的Consumer包裝成一個Observer(看清不是Observable!Observer是訂閱者),內(nèi)部就是簡單的在各個階段調(diào)用我們傳進去的Consumeraccpet方法。

Observer其實只是個接口,里面定義了接收到被觀察者(Observable)發(fā)出的事件時,訂閱者(Observer)應該執(zhí)行的方法:

public interface Observer<T> {
    void onSubscribe(Disposable d);
    void onNext(T t);
    void onError(Throwable e);
    void onComplete();
}

接著就是直接調(diào)用了subscribeActual方法。剛才我們在上述的步驟也說了,這個方法是Observable的抽象方法。

其實到這里我們可以看出,整個步驟通過對象的嵌套,形成了一條完整的鏈

創(chuàng)建任務鏈.png

逆向逐級訂閱

跟蹤subscribe

按照我們剛才的案例,到最后subscribe方法的調(diào)用關系應該是這樣的:

ObservableObserveOn.subscribe(LambdaObserver)。

所以我們跟進看一下ObservableObserveOn.subscribe方法的實現(xiàn):

@Override
protected void subscribeActual(Observer<? super T> observer) {
    //省略部分代碼
    Scheduler.Worker w = scheduler.createWorker();
    source.subscribe(new ObserveOnObserver<T>(observer, w, delayError, bufferSize));
}

可以看到,這里通過Scheduler創(chuàng)建了一個wroker對象,然后調(diào)用了source(上一級)的subscribe方法,并通過已有的observer對象生成一個ObserveOnObserver(注意是Observer)對象作為傳參。

看到這里也大概知道套路了= =和剛才一樣,會一直沿著整條鏈返回,一個一個訂閱對應Observable并生成新的嵌套的Observer

我們依舊跟著看看,ObservableObserveOn.subscribe之后是ObservableSubscribeOn.subscribe

@Override
public void subscribeActual(final Observer<? super T> s) {
    //將上一級傳進來的訂閱者包裝為線程安全的原子變量
    //SubscribeOnObserver只是簡單的包裝,這里不展開
    final SubscribeOnObserver<T> parent = new SubscribeOnObserver<T>(s);
    //先在當前線程執(zhí)行訂閱者的onSubscribe方法
    s.onSubscribe(parent);
    //然后在指定的線程中執(zhí)行source(上一級)的subscribe
    parent.setDisposable(scheduler.scheduleDirect(new Runnable() {
        @Override
        public void run() {
            source.subscribe(parent);
        }
    }));
}

根據(jù)我們最開始的業(yè)務邏輯,我們這里的scheduler應該對應IO線程,也就是說往下執(zhí)行的subscribe操作都是執(zhí)行再IO線程中的。(現(xiàn)在是逆向遍歷剛才建立的observable鏈。)

緊接著ObservableDoOnEach.subscribe

@Override
public void subscribeActual(Observer<? super T> t) {
    source.subscribe(new ObservableDoOnEach.DoOnEachObserver<T>(t, onNext, onError, onComplete, onAfterTerminate));
}

可以看到,這里也是封裝了我們傳進去的Consumer參數(shù),直接調(diào)用了上一級的source.subscribe方法。

= =那么就接著往下看。應該來到了ObservableMap.subscribe方法了。

@Override
public void subscribeActual(Observer<? super U> t) {
    source.subscribe(new ObservableMap.MapObserver<T, U>(t, function));
}

可以看到也是封裝了我們傳進去的Function參數(shù),然后調(diào)用上一級source.subscribe,也就是ObservableCreate.subscribe,也就到了鏈的一開始。

我們跟進看看ObservableCreate.subscribe

@Override
protected void subscribeActual(Observer<? super T> observer) {
    //首先是創(chuàng)建了CreateEmitter對象,這個類有沒有覺得特別眼熟- -
    ObservableCreate.CreateEmitter<T> parent = new ObservableCreate.CreateEmitter<T>(observer);
    //然后調(diào)用了訂閱者observer的onSubscribe方法
    //這里的訂閱者來自剛才的map操作
    observer.onSubscribe(parent);

    try {
        //調(diào)用上一級source的subscribe方法
        //顯然- -沒有上一級了,這里的source就是我們一開始創(chuàng)建的observer對象,調(diào)用的subscribe方法也就是我們調(diào)用的login()方法的地方
        source.subscribe(parent);
    } catch (Throwable ex) {
        Exceptions.throwIfFatal(ex);
        //捕獲異常
        parent.onError(ex);
    }
}

終于回到了第一級,可以看到,一樣的封裝了observer訂閱者,(這里的訂閱者來自map操作),然后調(diào)用了source.subscribe方法,(- -看到這里不知道你們還記不記得source來自哪- -看下面代碼)這個source來自我們一開始調(diào)用Observable.create時傳進來的參數(shù),而subscribe方法就是我們一開始執(zhí)行l(wèi)ogin()方法的地方。

Observable.create(new ObservableOnSubscribe<LoginApiResult>() {
            @Override
            public void subscribe(ObservableEmitter<LoginApiResult> e) throws Exception {
                e.onNext(login());
            }
        }) //調(diào)用登錄接口
        ……省略

也就是說,在剛才所有的逆序遍歷過程中,下一級的Observable會生成的對應的Observer訂閱上一級的source

逆向逐級訂閱.png

執(zhí)行任務鏈

接下來就是激動人心的執(zhí)行我們定義的任務了。(md終于- -)

= =在分析前,先重新看一下我們剛才的業(yè)務邏輯:

Observable.create(new ObservableOnSubscribe<LoginApiResult>() {
            @Override
            public void subscribe(ObservableEmitter<LoginApiResult> e) throws Exception {
                e.onNext(login());
            }
        }) //調(diào)用登錄接口
        .map(new Function<LoginApiBean, UserInfoBean>() {
            @Override
            protected UserInfoBean decode(LoginApiBean loginApiBean) {
                //處理登錄結(jié)果,返回UserInfo
                if (loginApiBean.isSuccess()) {
                    return loginApiBean.getUserInfoBean();
                } else {
                    throw new RequestFailException("獲取網(wǎng)絡請求失敗");
                }
            }
        })
        .doOnNext(new Consumer<UserInfoBean>() {    //保存登錄結(jié)果UserInfo
            @Override
            public void accept(@NonNull UserInfoBean bean) throws Exception {
                saveUserInfo(bean);
            }
        })
        .subscribeOn(Schedulers.io())   //調(diào)度線程
        .observeOn(AndroidSchedulers.mainThread())  //調(diào)度線程
        .subscribe(new Consumer<UserInfoBean>() {
            @Override
            public void accept(@NonNull UserInfoBean bean) throws Exception {LoginApiBean
                //整個請求成功,根據(jù)獲取的UserInfo更新對應的View
                showSuccessView(bean);
            }
        }, new Consumer<Throwable>() {
            @Override
            public void accept(@NonNull Throwable throwable) throws Exception {
                //請求失敗,顯示對應的View
                showFailView();
            }
        });

一趟創(chuàng)建,一趟逆向訂閱,我們又回到了最開始的地方。我們剛才分析到,ObservableCreate會執(zhí)行我們定義的方法

所以就來到了這段代碼:

public void subscribe(ObservableEmitter<LoginApiResult> e) throws Exception {
    e.onNext(login());
}

行- -就是login,就是調(diào)用ObservableEmitter.onNext方法。我們跟進:

public final class ObservableCreate<T> extends Observable<T> {
    
    protected void subscribeActual(Observer<? super T> observer) {
        //可以看到,這里傳入的Observer參數(shù)是來自下一級的訂閱者
        CreateEmitter<T> parent = new CreateEmitter<T>(observer);
        //省略一堆- -
    }


    //省略繼承關系
    static final class CreateEmitter<T> {
        //保存訂閱者
        CreateEmitter(Observer<? super T> observer) {
            this.observer = observer;
        }

        @Override
        public void onNext(T t) {
            //省略判空
            if (!isDisposed()) {
                //調(diào)用訂閱者的onNext方法
                observer.onNext(t);
            }
         }

    }
}

可以看到吧,簡單的執(zhí)行一些判斷后,就調(diào)用了訂閱者的onNext方法,而通過上面的代碼,我們可以看到observer來自于subscribe時調(diào)用構(gòu)造函數(shù)的傳參,而通過上述的分析,我們知道,這里的訂閱者來自下一級,也就是map操作生成的訂閱者。這里很自然的進入了map操作。
(后面不再貼出observer的來源)

我們再往下看到MapObserver

@Override
public void onNext(T t) {
    //省略一些細節(jié)上的判斷
    U v;
    //mapper就是我們new 的function對象
    v = mapper.apply(t)
    actual.onNext(v);
}

可以看到,這里調(diào)用了我們定義的apply方法,獲得了新的對象,然后調(diào)用了下一級訂閱者的onNext方法。

嘿嘿嘿看到這里大概就知道執(zhí)行任務鏈的套路了。嵌套的調(diào)用下一級的onNext方法。
我們先繼續(xù)往下看,來到了DoOnEachObserver中:

@Override
public void onNext(T t) {
    onNext.accept(t);
    actual.onNext(t);
}

bingo!基本上和我們猜想的一樣~accept方法就是我們定義的doOnNext的操作啊~

再接著往下來到SubscribeOnObserver

@Override
public void onNext(T t) {
  actual.onNext(t);
}

這貨更直接。。直接就調(diào)過去了- -(這里涉及到Scheduler的線程調(diào)度,后面再補充)

快到重點了,再看一下ObserveOnObserver

@Override
public void onNext(T t) {
    if (sourceMode != QueueDisposable.ASYNC) {
        queue.offer(t);
    }
    schedule();
}

擦,這貨邏輯賊復雜。。畢竟在這里進行了線程調(diào)度。暫時不深入。
只需要知道:這貨把任務提交給了Scheduler中的worker。等到任務結(jié)束獲取到結(jié)果后會調(diào)用下一級的onNext方法。

強行來到最后一層了~
這里的Observer就是我們調(diào)用subscribe時傳入的Observer啦~
那就是調(diào)用:

@Override
public void accept(@NonNull UserInfoBean bean) throws Exception {LoginApiBean
   //整個請求成功,根據(jù)獲取的UserInfo更新對應的View
   showSuccessView(bean);
}

行了- -走完整個流程了。。相信看到這里就能大致理解Rx的流程怎么走了(也沒有想象的那么復雜嘛~)

在剛才的遍歷訂閱后,每一步操作都會通知對應的Observer,從而完成整調(diào)任務鏈。

執(zhí)行任務鏈.png

總結(jié)

總結(jié)一下:

  1. 創(chuàng)建任務鏈,每一步都會返回對應的Observable對象。
  2. 逆向逐級訂閱。每一步都會生成對應的Observer對上一步生成的Observable進行訂閱
  3. 執(zhí)行任務鏈。執(zhí)行任務鏈之后,每一步都會通知對應的Observer,從而完成整調(diào)任務鏈。

嘿嘿嘿,感覺整個流程也沒有想想中的難~對Rx的理解又更上一層了。
= =呃。寫到這發(fā)現(xiàn)篇幅略長。Scheduler的解析還是另起一篇文章吧,挖個坑先。

[筆者仍為Android初學者。如有解釋錯誤的地方,歡迎評論區(qū)指正探討]


最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點,簡書系信息發(fā)布平臺,僅提供信息存儲服務。
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌,老刑警劉巖,帶你破解...
    沈念sama閱讀 228,030評論 6 531
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場離奇詭異,居然都是意外死亡,警方通過查閱死者的電腦和手機,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 98,310評論 3 415
  • 文/潘曉璐 我一進店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人,你說我怎么就攤上這事。” “怎么了?”我有些...
    開封第一講書人閱讀 175,951評論 0 373
  • 文/不壞的土叔 我叫張陵,是天一觀的道長。 經(jīng)常有香客問我,道長,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 62,796評論 1 309
  • 正文 為了忘掉前任,我火速辦了婚禮,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘。我一直安慰自己,他們只是感情好,可當我...
    茶點故事閱讀 71,566評論 6 407
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著,像睡著了一般。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 55,055評論 1 322
  • 那天,我揣著相機與錄音,去河邊找鬼。 笑死,一個胖子當著我的面吹牛,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播,決...
    沈念sama閱讀 43,142評論 3 440
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了?” 一聲冷哼從身側(cè)響起,我...
    開封第一講書人閱讀 42,303評論 0 288
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后,有當?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 48,799評論 1 333
  • 正文 獨居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 40,683評論 3 354
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發(fā)現(xiàn)自己被綠了。 大學時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點故事閱讀 42,899評論 1 369
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情,我是刑警寧澤,帶...
    沈念sama閱讀 38,409評論 5 358
  • 正文 年R本政府宣布,位于F島的核電站,受9級特大地震影響,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點故事閱讀 44,135評論 3 347
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧,春花似錦、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 34,520評論 0 26
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 35,757評論 1 282
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留,地道東北人。 一個月前我還...
    沈念sama閱讀 51,528評論 3 390
  • 正文 我出身青樓,卻偏偏與公主長得像,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當晚...
    茶點故事閱讀 47,844評論 2 372

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