初識Rxjava

星球話題:用過RxJava和RxAndroid嗎?RxAndroid切換線程是怎么實現(xiàn)的呢?

去年知乎上參加了玉剛的Live贸伐,聽大神講解職業(yè)規(guī)劃。隨后入了微信群怔揩,去年11月份也加入主席的星球捉邢。由于去年十一月份剛好接了外包工作,比較忙就忽略星球的任務(wù)商膊,說來慚愧伏伐,到現(xiàn)在還沒有交過一次作業(yè)。再加上今年年初想換工作翘狱,就忙于復(fù)習(xí)秘案,星球的作業(yè)就落下,希望從今天開始潦匈,把作業(yè)補回來阱高。

年初也去試水,發(fā)覺現(xiàn)在android的要求真的是高茬缩〕嗑可能也是自己比較菜吧,試了三家沒有拿到offer凰锡。今年計劃未舟,好好復(fù)習(xí)安卓知識圈暗,學(xué)點RN、小程序裕膀、PWA员串,爭取拿到好的offer。女朋友說我昼扛,晚上想了千萬條路寸齐,隔天起來走原路。哎抄谐,反正還是得腳踏實地渺鹦,一步一步學(xué)習(xí)。說干就干蛹含,下邊我們開始學(xué)習(xí)毅厚。

(1)RxJava 基本概念

1、Observable (可觀察者浦箱,即被觀察者)

2吸耿、Observer (觀察者)

3、subscribe (訂閱)憎茂、事件

4珍语、Scheduler 調(diào)度器,相當(dāng)于線程控制器

Rxjava 實現(xiàn)
1竖幔、創(chuàng)建Observable(被觀察者):

     mObservable = Observable.create(new Observable.OnSubscribe<String>() {
            @Override
            public void call(Subscriber<? super String> subscriber) {
                subscriber.onNext("2018年");
                subscriber.onNext("CBA");
                subscriber.onNext("遼寧隊");
                subscriber.onNext("奪冠");
                subscriber.onCompleted();
            }
        });

這里傳入了一個OnSubscribe對象作為參數(shù)板乙。OnSubscribe會被存儲在返回的 Observable對 象中,它的作用相當(dāng)于一個計劃表拳氢,當(dāng)Observable被訂閱的時候募逞,OnSubscribe的call()方法會自動被調(diào)用,事件序列就會依照設(shè)定依次觸發(fā)(對于上面的代碼馋评,就是觀察者Subscriber 將會被調(diào)用四次 onNext() 和一次 onCompleted())放接。這樣,由被觀察者調(diào)用了觀察者的回調(diào)方法留特,就實現(xiàn)了由被觀察者向觀察者的事件傳遞纠脾。

2、創(chuàng)建Observer(觀察者):

    mObserver = new Observer<String>() {
            @Override
            public void onCompleted() {
                LogUtil.d("onCompleted:");
            }
            @Override
            public void onError(Throwable e) {
                LogUtil.d("onError:"+e);
            }

            @Override
            public void onNext(String s) {
                LogUtil.d("onNext:"+s);
            }

        };

Subscriber是實現(xiàn)Observer的抽象類蜕青,用法也一樣:

    mSubscriber = new Subscriber() {
            @Override
            public void onCompleted() {
                LogUtil.d("onCompleted:");
            }

            @Override
            public void onError(Throwable e) {
                LogUtil.d("onError:"+e);
            }

            @Override
            public void onNext(Object o) {
                LogUtil.d("onNext:"+o);
            }
        };

Subscirber與Observe 的區(qū)別是:
1苟蹈、onStart(): 這是 Subscriber 增加的方法。它會在 subscribe 剛開始右核,而事件還未發(fā)送之 前被調(diào)用慧脱,可以用于做一些準備工作。
2贺喝、unsubscribe(): 這是 Subscriber 所實現(xiàn)的另一個接口 Subscription 的方法菱鸥,用于取消訂閱宗兼。

3、subscribe()訂閱:

 mObservable.subscribe(mObserver);
或者
mObservable.subscribe(mSubscriber);

以上1氮采、2殷绍、3過程也可以寫成:

   Observable.create(new Observable.OnSubscribe<String>() {

          @Override
          public void call(Subscriber<? super String> subscriber) {
              subscriber.onNext("2018年");
              subscriber.onNext("CBA");
              subscriber.onNext("遼寧隊");
              subscriber.onNext("奪冠");
              subscriber.onCompleted();
          }
      }).subscribe(new Observer<String>() {
           @Override
           public void onCompleted() {
               LogUtil.d("onCompleted:");
           }
           @Override
           public void onError(Throwable e) {
               LogUtil.d("onError:"+e);
           }

           @Override
           public void onNext(String s) {
               LogUtil.d("onNext:"+s);
           }

       });

結(jié)果:


Rxjava結(jié)果.png

(2)Rxjava常見操作符
下邊我們來了解一下Rxjava 常見操作符:
just:將傳入的參數(shù)依次發(fā)送出來

Observable observable = Observable.just("2018年", "CBA", "遼寧隊","奪冠");
// 將會依次調(diào)用:
// onNext("2018年");
// onNext("CBA");
// onNext("遼寧隊");
// onNext("奪冠");
// onCompleted();

from(T[]) / from(Iterable<? extends T>) : 將傳入的數(shù)組或 Iterable 拆分成具體對象后,依次發(fā)送出:

String[] words = {"2018年", "CBA", "遼寧隊","奪冠"};
Observable observable = Observable.from(words);
// 將會依次調(diào)用:
// onNext("2018年");
// onNext("CBA");
// onNext("遼寧隊");
// onNext("奪冠");
// onCompleted();

(3)Rxjava如何切換線程:Scheduler
我們回到文章開頭鹊漠,RxAndroid切換線程是怎么實現(xiàn)的呢篡帕?看下邊一個例子:

Observable.create(new Observable.OnSubscribe<String>() {

            @Override
            public void call(Subscriber<? super String> subscriber) {
                LogUtil.d("subscriber:");
                subscriber.onNext("2018年");
                subscriber.onNext("CBA");
                subscriber.onNext("遼寧隊");
                subscriber.onNext("奪冠");
                subscriber.onCompleted();
            }
        })
                 .subscribeOn(Schedulers.newThread())
                 .observeOn(AndroidSchedulers.mainThread())
                 .subscribe(new Observer<String>() {
             @Override
             public void onCompleted() {
                 LogUtil.d("onCompleted:");
             }
             @Override
             public void onError(Throwable e) {
                 LogUtil.d("onError:"+e);
             }

             @Override
             public void onNext(String s) {
                 LogUtil.d("onNext:"+s);
             }

         });

subscribeOn(): 指定subscribe()所發(fā)生的線程,即 Observable.OnSubscribe被激活時所處的線程贸呢。或者叫做事件產(chǎn)生的線程拢军。
observeOn(): 指定 Subscriber 所運行在的線程楞陷。或者叫做事件消費的線程茉唉。
我們來了解一下Scheduler:
在不指定線程的情況下固蛾,RxJava遵循的是線程不變的原則,即:在哪個線程調(diào)用 subscribe(),就在哪個線程生產(chǎn)事件度陆;在哪個線程生產(chǎn)事件艾凯,就在哪個線程消費事件。如果需要切換線程懂傀,就需要用到Scheduler(調(diào)度器),下面是Scheduler的API:

1趾诗、Schedulers.immediate(): 直接在當(dāng)前線程運行,相當(dāng)于不指定線程蹬蚁。這是默認的 Scheduler恃泪。
2、Schedulers.newThread(): 總是啟用新線程犀斋,并在新線程執(zhí)行操作贝乎。
Schedulers.io(): I/O 操作(讀寫文件、讀寫數(shù)據(jù)庫叽粹、網(wǎng)絡(luò)信息交互等)所使用的 Scheduler览效。行為模式和 newThread() 差不多,區(qū)別在于 io() 的內(nèi)部實現(xiàn)是是用一個無數(shù)量上限的線程池虫几,可以重用空閑的線程锤灿,因此多數(shù)情況下 io() 比 newThread() 更有效率。不要把計算工作放在 io() 中持钉,可以避免創(chuàng)建不必要的線程衡招。
3、Schedulers.computation(): 計算所使用的 Scheduler每强。這個計算指的是 CPU 密集型計算始腾,即不會被 I/O 等操作限制性能的操作州刽,例如圖形的計算。這個 Scheduler 使用的固定的線程池浪箭,大小為 CPU 核數(shù)穗椅。不要把 I/O 操作放在 computation() 中,否則 I/O 操作的等待時間會浪費 CPU奶栖。
4匹表、Android 還有一個專用的 AndroidSchedulers.mainThread(),它指定的操作將在 Android 主線程運行宣鄙。
有了這幾個 Scheduler 袍镀,就可以使用 subscribeOn() 和 observeOn() 兩個方法來對線程進行控制了。

那么我們有個疑問冻晤,Rxjava內(nèi)部是如何切換線程?首先我們來看subscribeOn()

subscribeOn()源碼:

  public final Observable<T> subscribeOn(Scheduler scheduler) {
        if (this instanceof ScalarSynchronousObservable) {
            return ((ScalarSynchronousObservable<T>)this).scalarScheduleOn(scheduler);
        }
        return create(new OperatorSubscribeOn<T>(this, scheduler));
    }

我們看到參數(shù)是傳入Scheduler 調(diào)度器苇羡,然后創(chuàng)建了新的Observable,我們看到OperatorSubscribeOn這個對象鼻弧,OperatorSubscribeOn原始Observable對象和調(diào)度器scheduler设江,那么這個OperatorSubscribeOn是什么呢,我們看下源碼:

public final class OperatorSubscribeOn<T> implements OnSubscribe<T> {

    final Scheduler scheduler;   //調(diào)度器
    final Observable<T> source; //原始Observable

    public OperatorSubscribeOn(Observable<T> source, Scheduler scheduler) {
        this.scheduler = scheduler;
        this.source = source;
    }
//①.原始觀察者訂閱了新的Observable后攘轩,將執(zhí)行此call方法
    @Override
    public void call(final Subscriber<? super T> subscriber) {
        final Worker inner = scheduler.createWorker();
        subscriber.add(inner);
          //②. call方法中使用傳入的調(diào)度器創(chuàng)建的Worker對象的schedule方法切換線程
        inner.schedule(new Action0() {
            @Override
            public void call() {
                final Thread t = Thread.currentThread();
                  //③ .創(chuàng)建了一個新的觀察者
                Subscriber<T> s = new Subscriber<T>(subscriber) {
                    @Override
                    public void onNext(T t) {
                               //⑤. 新的觀察者收到數(shù)據(jù)后直接發(fā)送給原始觀察者
                        subscriber.onNext(t);
                    }
                    
                    @Override
                    public void onError(Throwable e) {
                        try {
                   //⑤. 新的觀察者收到數(shù)據(jù)后直接發(fā)送給原始觀察者
                            subscriber.onError(e);
                        } finally {
                            inner.unsubscribe();
                        }
                    }
                    
                    @Override
                    public void onCompleted() {
                        try {
                            subscriber.onCompleted();
                        } finally {
                            inner.unsubscribe();
                        }
                    }
                    
                    @Override
                    public void setProducer(final Producer p) {
                        subscriber.setProducer(new Producer() {
                            @Override
                            public void request(final long n) {
                                if (t == Thread.currentThread()) {
                                    p.request(n);
                                } else {
                                    inner.schedule(new Action0() {
                                        @Override
                                        public void call() {
                                            p.request(n);
                                        }
                                    });
                                }
                            }
                        });
                    }
                };
                //④. 在切換的線程中叉存,新的觀察者訂閱原始Observable,用來接收數(shù)據(jù)
                source.unsafeSubscribe(s);
            }
        });
    }
}

OperatorSubscribeOn是實現(xiàn)Observable的OnSubscribe 接口

  public interface OnSubscribe<T> extends Action1<Subscriber<? super T>> {
        // cover for generics insanity
    }
public interface Action1<T> extends Action {
    void call(T t);
}

上面源碼中注釋已經(jīng)寫的很清楚了度帮,OperatorSubscribeOn字意上來理解歼捏,Operator操作員,相當(dāng)于媒介笨篷,為新的Observable發(fā)射數(shù)據(jù)甫菠。它創(chuàng)建了一個新的觀察者訂閱原始Observable,這樣就可以接受原始Observable發(fā)射的數(shù)據(jù)冕屯,然后直接發(fā)送給原始觀察者寂诱。
所以O(shè)peratorSubscribeOn也是間接實現(xiàn)了Action1,我們來看OperatorSubscribeOn在call()方法里邊操作了什么安聘。在call方法中通過scheduler.createWorker().schedule()完成線程的切換痰洒,這里就牽扯到兩個對象了,Scheduler和Worker浴韭。Scheduler是個抽象類丘喻,是從外邊傳進來的。我們就看一個簡單的Schedulers.newThread()念颈,其他也是從類似泉粉,下面一步一步看源碼:

/**
 * Static factory methods for creating Schedulers.
 */
public final class Schedulers {
  //各種調(diào)度器對象
    private final Scheduler computationScheduler;
    private final Scheduler ioScheduler;
    private final Scheduler newThreadScheduler;

    private static final AtomicReference<Schedulers> INSTANCE = new AtomicReference<Schedulers>();

         ......
   //構(gòu)造方法
    private Schedulers() {
        RxJavaSchedulersHook hook = RxJavaPlugins.getInstance().getSchedulersHook();

          ......

        Scheduler nt = hook.getNewThreadScheduler();
        if (nt != null) {
            newThreadScheduler = nt;
        } else {
       //①.創(chuàng)建newThreadScheduler對象
            newThreadScheduler = RxJavaSchedulersHook.createNewThreadScheduler();
        }
    }
}

 //②. 獲取NewThreadScheduler對象
 public static Scheduler newThread() {
        return getInstance().newThreadScheduler;
    }

Schedulers中保存了幾個調(diào)度器對象,在Schedulers被加載的時候,他們就被初始化了嗡靡,Schedulers就像是一個調(diào)度器的控制器跺撼,跟蹤newThreadScheduler,看到newThreadScheduler在RxJavaSchedulersHook.createNewScheduler()實例化讨彼。CTRL+鼠標左鍵跟createNewScheduler()方法進去歉井,最終調(diào)到NewThreadScheduler(ThreadFactory threadFactory)的方法:

**
 * Schedules work on a new thread.
 */
public final class NewThreadScheduler extends Scheduler {
    private final ThreadFactory threadFactory;

    public NewThreadScheduler(ThreadFactory threadFactory) {
        this.threadFactory = threadFactory;
    }

    @Override
    public Worker createWorker() {
        return new NewThreadWorker(threadFactory);
    }
}

NewThreadScheduler就是我們調(diào)用subscribeOn(Schedulers.newThread() )傳入的調(diào)度器對象,每個調(diào)度器對象都有一個createWorker方法用于創(chuàng)建一個Worker對象哈误,而NewThreadScheduler對應(yīng)創(chuàng)建的Worker是一個叫NewThreadWorker的對象哩至,在新產(chǎn)生的OperatorSubscribeOn計劃表中就是通過NewThreadWorker.schedule(Action0)實現(xiàn)線程的切換,下面我們跟蹤schedule(Action0)方法:

public class NewThreadWorker extends Scheduler.Worker implements Subscription {
    private final ScheduledExecutorService executor;   //
    public NewThreadWorker(ThreadFactory threadFactory) {
        //創(chuàng)建一個線程池
        ScheduledExecutorService exec = Executors.newScheduledThreadPool(1, threadFactory);
        executor = exec;
    }
    @Override
    public Subscription schedule(final Action0 action) {
        return schedule(action, 0, null);
    }
    @Override
    public Subscription schedule(final Action0 action, long delayTime, TimeUnit unit) {
        return scheduleActual(action, delayTime, unit);
    }
    //重要:worker.schedule()最終調(diào)用的是這個方法
    public ScheduledAction scheduleActual(final Action0 action, long delayTime, TimeUnit unit) {
        //return action;
        Action0 decoratedAction = schedulersHook.onSchedule(action);
        //ScheduledAction就是一個Runnable對象蜜自,在run()方法中調(diào)用了Action0.call()
        ScheduledAction run = new ScheduledAction(decoratedAction);
        Future<?> f;
        if (delayTime <= 0) {
            f = executor.submit(run);   //將Runnable對象放入線程池中
        } else {
            f = executor.schedule(run, delayTime, unit);  //延遲執(zhí)行
        }
        run.add(f);

        return run;
    }
    ...
}

我們發(fā)現(xiàn)OperatorSubscribeOn計劃表中通過NewThreadWorker.schedule(Action0)菩貌,將Action0放入到一個線程池中執(zhí)行,這樣就實現(xiàn)了線程的切換重荠。

多次subscribeOn()的情況:
我們發(fā)現(xiàn)菜谣,每次使用subscribeOn都會產(chǎn)生一個新的Observable,并產(chǎn)生一個新的計劃表OnSubscribe晚缩,目標Subscriber最后訂閱的將是最后一次subscribeOn產(chǎn)生的新的Observable。在每個新的OnSubscribe的call方法中都會有一個產(chǎn)生一個新的線程媳危,在這個新線程中訂閱上一級Observable荞彼,并創(chuàng)建一個新的Subscriber接受數(shù)據(jù),最終原始Observable將在第一個新線程中發(fā)射數(shù)據(jù)待笑,然后傳送給給下一個新的觀察者鸣皂,直到傳送到目標觀察者,所以多次調(diào)用subscribeOn只有第一個起作用(這只是表面現(xiàn)象暮蹂,其實每個subscribeOn都切換了線程寞缝,只是最終目標Observable是在第一個subscribeOn產(chǎn)生的線程中發(fā)射數(shù)據(jù)的)。
?多次subscribeOn()只有第一個會起作用仰泻,后面的只是在第一個的基礎(chǔ)上在外面套了一層殼荆陆,就像下面的偽代碼,最后執(zhí)行是在第一個新線程中執(zhí)行:

...
//第3個subscribeOn產(chǎn)生的新線程
new Thread(){
    @Override
    public void run() {
        Subscriber s1 = new Subscriber();
        //第2個subscribeOn產(chǎn)生的新線程
        new Thread(){
            @Override
            public void run() {
                Subscriber s2 = new Subscriber();
                //第1個subscribeOn產(chǎn)生的新線程
                new Thread(){
                    @Override
                    public void run() {
                        Subscriber<T> s3 = new Subscriber<T>(subscriber) {
                            @Override
                            public void onNext(T t) {
                                subscriber.onNext(t);
                            }
                            ...
                        };
                        //①. 最后一個新觀察者訂閱原始Observable
                        原始Observable.subscribe(s3);
                        //②. 原始Observable將在此線程中發(fā)射數(shù)據(jù)

                              //③. 最后一個新的觀察者s3接受數(shù)據(jù)

                              //④. s3收到數(shù)據(jù)后集侯,直接發(fā)送給s2被啼,s2收到數(shù)據(jù)后傳給s1,...最后目標觀察者收到數(shù)據(jù)
                         } 
                }.start();
            }
        }.start();
    }
}.start();

observeOn原理:
observeOn調(diào)用的是lift操作符,lift操作符棠枉。lift有點難理解浓体,簡單點說就是在 Observable 執(zhí)行了 lift(Operator) 方法之后,會返回一個新的 Observable辈讶,這個新的 Observable 會像一個代理一樣命浴,負責(zé)接收原始的 Observable 發(fā)出的事件,并在處理后發(fā)送給 Subscriber。

observeOn一樣創(chuàng)建了一個代理的Observable生闲,并創(chuàng)建一個代理觀察者接受上一級Observable的數(shù)據(jù)媳溺,代理觀察者收到數(shù)據(jù)之后會開啟一個線程,在新的線程中跪腹,調(diào)用下一級觀察者的onNext褂删、onCompete、onError方法冲茸。

我們看看observeOn操作符的源碼:

public final class OperatorObserveOn<T> implements Observable.Operator<T, T> {
    private final Scheduler scheduler;
    //創(chuàng)建代理觀察者屯阀,用于接收上一級Observable發(fā)射的數(shù)據(jù)
    @Override
    public Subscriber<? super T> call(Subscriber<? super T> child) {
        if (scheduler instanceof ImmediateScheduler) {
            return child;
        } else if (scheduler instanceof TrampolineScheduler) {
            return child;
        } else {
            ObserveOnSubscriber<T> parent = new ObserveOnSubscriber<T>(scheduler, child, delayError, bufferSize);
            parent.init();
            return parent;
        }
    }

    //代理觀察者
    private static final class ObserveOnSubscriber<T> extends Subscriber<T> implements Action0 {
        final Subscriber<? super T> child;
        final Scheduler.Worker recursiveScheduler;
        final NotificationLite<T> on;
        final Queue<Object> queue;
        //接受上一級Observable發(fā)射的數(shù)據(jù)
        @Override
        public void onNext(final T t) {
            if (isUnsubscribed() || finished) {
                return;
            }
            if (!queue.offer(on.next(t))) {
                onError(new MissingBackpressureException());
                return;
            }
            schedule();
        }
        @Override
        public void onCompleted() {
            ...
            schedule();
        }
        @Override
        public void onError(final Throwable e) {
            ...
            schedule();
        }
        //開啟新線程處理數(shù)據(jù)
        protected void schedule() {
            if (counter.getAndIncrement() == 0) {
                recursiveScheduler.schedule(this);
            }
        }
        // only execute this from schedule()
        //在新線程中將數(shù)據(jù)發(fā)送給目標觀察者
        @Override
        public void call() {
            long missed = 1L;
            long currentEmission = emitted;
            final Queue<Object> q = this.queue;
            final Subscriber<? super T> localChild = this.child;
            final NotificationLite<T> localOn = this.on;
            for (;;) {
                while (requestAmount != currentEmission) {
                    ...
                    localChild.onNext(localOn.getValue(v));
                }
            }
        }
    }
}

ObserveOnSubscriber代理觀察者相當(dāng)于微信代理商,讓代理商幫忙買onNext轴术,onCompleted难衰,onError,代理商分別代表你進行購買逗栽。我們看到Worker類的 recursiveScheduler執(zhí)行recursiveScheduler.schedule(this)盖袭,回調(diào)到方法call中。call可以由傳進來的schedule 實現(xiàn)線程切換彼宠。就像上邊的NewThreadWorker.schedule(Action0)一樣鳄虱。
代理的OnSubscribe中的call方法就是讓代理Subscriber訂閱上一級Observable,直到訂閱到原始Observable發(fā)射數(shù)據(jù)凭峡,代理Subscriber收到數(shù)據(jù)后拙已,可能對數(shù)據(jù)做一些操作,然后將數(shù)據(jù)傳送給下一級Subscriber摧冀,直到目標觀察者接收到數(shù)據(jù)倍踪,目標觀察者在那個線程接受數(shù)據(jù)取決于上一個Subscriber在哪一個線程調(diào)用目標觀察者。
嗯索昂,本人技術(shù)有限建车,也是參考以下文章學(xué)習(xí)的。也是希望今年能夠耐心椒惨、堅持學(xué)下去吧缤至。希望在2018年想找個好工作的小伙伴們,共勉康谆、堅持凄杯!
給 Android 開發(fā)者的 RxJava 詳解
https://blog.csdn.net/xmxkf/article/details/51821940

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市秉宿,隨后出現(xiàn)的幾起案子戒突,更是在濱河造成了極大的恐慌,老刑警劉巖描睦,帶你破解...
    沈念sama閱讀 210,914評論 6 490
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件膊存,死亡現(xiàn)場離奇詭異,居然都是意外死亡,警方通過查閱死者的電腦和手機隔崎,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 89,935評論 2 383
  • 文/潘曉璐 我一進店門今艺,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人爵卒,你說我怎么就攤上這事虚缎。” “怎么了钓株?”我有些...
    開封第一講書人閱讀 156,531評論 0 345
  • 文/不壞的土叔 我叫張陵实牡,是天一觀的道長。 經(jīng)常有香客問我轴合,道長创坞,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 56,309評論 1 282
  • 正文 為了忘掉前任受葛,我火速辦了婚禮题涨,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘总滩。我一直安慰自己纲堵,他們只是感情好,可當(dāng)我...
    茶點故事閱讀 65,381評論 5 384
  • 文/花漫 我一把揭開白布闰渔。 她就那樣靜靜地躺著席函,像睡著了一般。 火紅的嫁衣襯著肌膚如雪澜建。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 49,730評論 1 289
  • 那天蝌以,我揣著相機與錄音炕舵,去河邊找鬼。 笑死跟畅,一個胖子當(dāng)著我的面吹牛咽筋,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播徊件,決...
    沈念sama閱讀 38,882評論 3 404
  • 文/蒼蘭香墨 我猛地睜開眼奸攻,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了虱痕?” 一聲冷哼從身側(cè)響起睹耐,我...
    開封第一講書人閱讀 37,643評論 0 266
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎部翘,沒想到半個月后硝训,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 44,095評論 1 303
  • 正文 獨居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 36,448評論 2 325
  • 正文 我和宋清朗相戀三年窖梁,在試婚紗的時候發(fā)現(xiàn)自己被綠了赘风。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點故事閱讀 38,566評論 1 339
  • 序言:一個原本活蹦亂跳的男人離奇死亡纵刘,死狀恐怖邀窃,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情假哎,我是刑警寧澤瞬捕,帶...
    沈念sama閱讀 34,253評論 4 328
  • 正文 年R本政府宣布,位于F島的核電站位谋,受9級特大地震影響山析,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜掏父,卻給世界環(huán)境...
    茶點故事閱讀 39,829評論 3 312
  • 文/蒙蒙 一笋轨、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧赊淑,春花似錦爵政、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,715評論 0 21
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至饱岸,卻和暖如春掺出,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背苫费。 一陣腳步聲響...
    開封第一講書人閱讀 31,945評論 1 264
  • 我被黑心中介騙來泰國打工汤锨, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留,地道東北人百框。 一個月前我還...
    沈念sama閱讀 46,248評論 2 360
  • 正文 我出身青樓闲礼,卻偏偏與公主長得像,于是被迫代替她去往敵國和親铐维。 傳聞我的和親對象是個殘疾皇子柬泽,可洞房花燭夜當(dāng)晚...
    茶點故事閱讀 43,440評論 2 348

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