Android源碼解析Handler系列第(三)篇---深入了解Android的消息機(jī)制

轉(zhuǎn)載請(qǐng)注明文章出處LooperJing担忧!

Android的消息機(jī)制我覺(jué)得是每一個(gè)弄Android開(kāi)發(fā)的人都要弄懂得問(wèn)題,也有很多人對(duì)它進(jìn)行研究,Android的消息機(jī)制的重要性不強(qiáng)調(diào),但是覺(jué)得自己對(duì)Android的消息機(jī)制了解不深刻锈颗,所以決定深入源碼,寫(xiě)下三篇博客以記之咪惠。因?yàn)镸essage全局池和ThreadLocal對(duì)Android的消息機(jī)制理解很重要击吱,附上前兩篇的博客地址。
Android源碼解析Handler系列第(一)篇 --- Message全局池
Android源碼解析Handler系列第(二)篇 --- ThreadLocal詳解

先來(lái)一張圖感受一下遥昧,下面這張圖基本說(shuō)明了Android的消息機(jī)制的工作流程覆醇。


Android的消息機(jī)制

不看別的,主要涉及的類(lèi)有Thread炭臭,Looper永脓,MessageQueue,Handler,其實(shí)還有一個(gè)ThreadLocal,這些類(lèi)都是整套消息機(jī)制中很關(guān)鍵的類(lèi)鞋仍,下面我們正式分析這套機(jī)制是怎么運(yùn)行的常摧。

要分析這套機(jī)制是怎么運(yùn)行的,要首先從應(yīng)用程序的入口說(shuō)起威创,Android應(yīng)用程序的入口是ActivityThread類(lèi)的main方法排宰。下面是main方法中的相關(guān)代碼。

 public static void main(String[] args) {
    

        Process.setArgV0("<pre-initialized>");

        //創(chuàng)建UI線(xiàn)程所需要的Looper,Looper有何用那婉,后面再說(shuō)
        Looper.prepareMainLooper();

        //這里最終啟動(dòng)應(yīng)用程序
        ActivityThread thread = new ActivityThread();
        thread.attach(false);

        if (sMainThreadHandler == null) {
            sMainThreadHandler = thread.getHandler();
        }

        if (false) {
            Looper.myLooper().setMessageLogging(new
                    LogPrinter(Log.DEBUG, "ActivityThread"));
        }
        //執(zhí)行消息循環(huán)
        Looper.loop();

        throw new RuntimeException("Main thread loop unexpectedly exited");
    }

注意到板甘,入口函數(shù)的最后一行代碼是Looper調(diào)用了靜態(tài)方法Looper. loop(),而這個(gè)loop是死循環(huán),按照正常的理解详炬,在主線(xiàn)程中執(zhí)行死循環(huán)盐类,那么就不能做其他任務(wù)了,比如按鈕事件的觸發(fā)呛谜,動(dòng)畫(huà)的播放等在跳。但是反過(guò)來(lái)想一想,如果這不是死循環(huán)隐岛,那么最后一行代碼執(zhí)行完猫妙,應(yīng)用程序不就跑完了嘛。并且這行代碼也只能放在main的最后一句聚凹,如果放在前面割坠,后面的程序就沒(méi)有辦法進(jìn)行了。

好滴妒牙, Looper.loop()是執(zhí)行消息循環(huán)彼哼,去這里看個(gè)究竟。

/**
   * Run the message queue in this thread. Be sure to call
   * {@link #quit()} to end the loop.
   */
  public static void loop() {
      final Looper me = myLooper();
      if (me == null) {
          throw new RuntimeException("No Looper; Looper.prepare() wasn't called on this thread.");
      }
      final MessageQueue queue = me.mQueue;

      // Make sure the identity of this thread is that of the local process,
      // and keep track of what that identity token actually is.
      Binder.clearCallingIdentity();
      final long ident = Binder.clearCallingIdentity();

      for (;;) {
          Message msg = queue.next(); // might block
          if (msg == null) {
              // No message indicates that the message queue is quitting.
              return;
          }

          // This must be in a local variable, in case a UI event sets the logger
          Printer logging = me.mLogging;
          if (logging != null) {
              logging.println(">>>>> Dispatching to " + msg.target + " " +
                      msg.callback + ": " + msg.what);
          }

          msg.target.dispatchMessage(msg);

          if (logging != null) {
              logging.println("<<<<< Finished to " + msg.target + " " + msg.callback);
          }

          // Make sure that during the course of dispatching the
          // identity of the thread wasn't corrupted.
          final long newIdent = Binder.clearCallingIdentity();
          if (ident != newIdent) {
              Log.wtf(TAG, "Thread identity changed from 0x"
                      + Long.toHexString(ident) + " to 0x"
                      + Long.toHexString(newIdent) + " while dispatching to "
                      + msg.target.getClass().getName() + " "
                      + msg.callback + " what=" + msg.what);
          }

          msg.recycleUnchecked();
      }
  }

代碼比較長(zhǎng)湘今,但是真的比較容易理解的敢朱,第一句執(zhí)行final Looper me = myLooper()獲取一個(gè)Looper對(duì)象,怎么獲取的呢?在myLooper中涉及到了ThreadLocal,對(duì)ThreadLocal不了解的拴签,看我的第二篇博客孝常。


  /**
     * Return the Looper object associated with the current thread.  Returns
     * null if the calling thread is not associated with a Looper.
     */
    public static @Nullable Looper myLooper() {
        return sThreadLocal.get();
    }

如果sThreadLocal.get()返回了NULl,那么程序就會(huì) throw new RuntimeException("No Looper; Looper.prepare() wasn't called on this thread."); 蚓哩,尼瑪构灸,如果主線(xiàn)程在這都拋了異常,那還玩毛線(xiàn)杖剪,所以必然有sThreadLocal.set()冻押。回到剛才盛嘿,在執(zhí)行消息循環(huán)Looper.loop之前洛巢,有一句關(guān)鍵的代碼, Looper.prepareMainLooper()次兆,這句話(huà)可以創(chuàng)建UI線(xiàn)程所需要的Looper稿茉,跟蹤進(jìn)去。

 public static void prepareMainLooper() {
        //代碼在下面
        prepare(false);
        synchronized (Looper.class) {
            if (sMainLooper != null) {
                throw new IllegalStateException("The main Looper has already been prepared.");
            }
          //從sThreadLocal中獲取Looper芥炭,系統(tǒng)默認(rèn)給我們創(chuàng)建了sMainLooper
            sMainLooper = myLooper();
        }
    }

  private static void prepare(boolean quitAllowed) {
        if (sThreadLocal.get() != null) {
            throw new RuntimeException("Only one Looper may be created per thread");
        }
        sThreadLocal.set(new Looper(quitAllowed));
    }

在sThreadLocal.get() 為NULL的情況下漓库,直接以餓漢式的方式new了一個(gè)Looper, 調(diào)用sThreadLocal的set方法,這一步园蝠,通過(guò)sThreadLocal渺蒿,其實(shí)是將Looper與當(dāng)前線(xiàn)程(UI線(xiàn)程)做了關(guān)聯(lián), 也就是說(shuō)上面創(chuàng)建的Looper對(duì)象me只能被主線(xiàn)程所訪問(wèn)彪薛,其他線(xiàn)程訪問(wèn)不了C啊!善延!少态,這就是ThreadLocal的厲害之處。Looper我們稱(chēng)之是消息循環(huán)器易遣,就是不斷的把消息從消息隊(duì)列中取出來(lái)彼妻,看一個(gè)創(chuàng)建Looper干了什么。

private Looper(boolean quitAllowed) {
        mQueue = new MessageQueue(quitAllowed);
        mThread = Thread.currentThread();
    }

主要是初始化了一個(gè)消息隊(duì)列豆茫,原來(lái)消息隊(duì)列是Looper里面的成員侨歉,記住了!@奖 为肮!,還保留了當(dāng)前線(xiàn)程肤京,有了mThread,我們就可以知道這個(gè)Looper是哪一個(gè)線(xiàn)程的Looper。Looper的成員非常少忘分,就幾個(gè)棋枕,看下面。

    // sThreadLocal.get() will return null unless you've called prepare().
     static final ThreadLocal<Looper> sThreadLocal = new ThreadLocal<Looper>();
   
     private static Looper sMainLooper;  // guarded by Looper.class

     final MessageQueue mQueue;
  
     final Thread mThread;

OK妒峦,在ActivityThread中的Looper.loop()這個(gè)死循環(huán)啟動(dòng)之前重斑,需要執(zhí)行 Looper.prepareMainLooper(),這個(gè)方法執(zhí)行以后肯骇,我們知道發(fā)生了以下幾件事情窥浪。

  • 1、以餓漢式的方式創(chuàng)建了主線(xiàn)程的Looper對(duì)象
  • 2笛丙、用sThreadLocal將主線(xiàn)程與這個(gè)Looper對(duì)象關(guān)聯(lián)起來(lái)
  • 3漾脂、sThreadLocal.get一個(gè)Looper對(duì)象,賦值給sMainLooper胚鸯。

繼續(xù)看Looper.loop()里面骨稿,第二句關(guān)鍵代碼是final MessageQueue queue = me.mQueue;即獲取Looper中的消息隊(duì)列,之后是一個(gè)死循環(huán)姜钳,對(duì)消息隊(duì)列里面的消息進(jìn)行遍歷坦冠,在這里我們先留一個(gè)問(wèn)題 消息是怎么存到消息隊(duì)列里面去的?

 for (;;) {
            Message msg = queue.next(); // might block
            if (msg == null) {
                // No message indicates that the message queue is quitting.
                return;
            }

            msg.target.dispatchMessage(msg);

            msg.recycleUnchecked();
        }

在這個(gè)死循環(huán)中遍歷哥桥,通過(guò)queue.next()取出消息辙浑。

  Message next() {
        
        for (;;) {
          
            //這句代碼水比較深,先跳過(guò)
            nativePollOnce(ptr, nextPollTimeoutMillis);

            synchronized (this) {
                // Try to retrieve the next message.  Return if found.
                final long now = SystemClock.uptimeMillis();
                Message prevMsg = null;
                Message msg = mMessages;
                if (msg != null && msg.target == null) {
                    // Stalled by a barrier.  Find the next asynchronous message in the queue.
                    do {
                        prevMsg = msg;
                        msg = msg.next;
                    } while (msg != null && !msg.isAsynchronous());
                }
                if (msg != null) {
                    if (now < msg.when) {
                        // Next message is not ready.  Set a timeout to wake up when it is ready.
                        nextPollTimeoutMillis = (int) Math.min(msg.when - now, Integer.MAX_VALUE);
                    } else {
                        // Got a message.
                        mBlocked = false;
                        if (prevMsg != null) {
                            prevMsg.next = msg.next;
                        } else {
                            mMessages = msg.next;
                        }
                        msg.next = null;
                        if (DEBUG) Log.v(TAG, "Returning message: " + msg);
                        msg.markInUse();
                        return msg;
                    }
                } else {
                    // No more messages.
                    nextPollTimeoutMillis = -1;
                }

            } 
        }
    }

在同步synchronized中拟糕,這一大段代碼也就是寫(xiě)消息何時(shí)出隊(duì)列的事情判呕。

if (now < msg.when) {
         // Next message is not ready.  Set a timeout to wake up when it is ready.
            nextPollTimeoutMillis = (int) Math.min(msg.when - now, Integer.MAX_VALUE);
        }

nextPollTimeoutMillis表示消息出隊(duì)列的時(shí)間,如果現(xiàn)在的時(shí)間now比Message要執(zhí)行的時(shí)間msg.when要小已卸,就減去現(xiàn)在的時(shí)間佛玄,計(jì)算一個(gè)新的時(shí)間給nextPollTimeoutMillis。如果消息的執(zhí)行時(shí)間已經(jīng)到了累澡,那么就取出這個(gè)消息返回并調(diào)用msg.markInUse()梦抢,把消息的狀態(tài)賦值為使用之中。

OK愧哟,在上面那個(gè)for循環(huán)中奥吩,消息是取到之后,就要進(jìn)行消息的分發(fā)了蕊梧,消息的分發(fā)也是有一套順序的霞赫。調(diào)用 msg.target.dispatchMessage(msg),進(jìn)行消息的分發(fā)肥矢,不知道target是什么的(是Handler對(duì)象端衰,標(biāo)識(shí)這個(gè)消息被誰(shuí)處理)叠洗,去看我的第一篇博客。

    /**
     * Handle system messages here.
     */
    public void dispatchMessage(Message msg) {
        if (msg.callback != null) {
            handleCallback(msg);
        } else {
            if (mCallback != null) {
                if (mCallback.handleMessage(msg)) {
                    return;
                }
            }
            handleMessage(msg);
        }
    }

這里具體可以看我第一篇博客中****Message的處理順序****章節(jié)旅东,可以知道Message是怎么進(jìn)行分發(fā)的灭抑,從消息的分發(fā)可以看到,如果沒(méi)有Runnable的類(lèi)型的消息callback以及沒(méi)有Callback類(lèi)型的回調(diào)mCallback,那么就會(huì)回調(diào)Handler的handleMessge抵代。

   private Handler  handler=new Handler(){
        @Override
        public void handleMessage(Message msg) {
           dosomething();
        }
    };

上面試我們常常寫(xiě)的代碼腾节,走到這,相信你已經(jīng)知道我們常寫(xiě)的那個(gè)套路是怎么來(lái)的了荤牍。在Looper.looper中還有最后一句代碼 msg.recycleUnchecked();這個(gè)就不用講了案腺,主要做消息的回收操作,這里消息會(huì)進(jìn)入消息的全局池中康吵。

上面留下了一個(gè)問(wèn)題****消息是怎么存到消息隊(duì)列里面去的劈榨?****現(xiàn)在來(lái)分析分析,這個(gè)從我們發(fā)送消息(handler.sendMessage())來(lái)入手涎才,經(jīng)過(guò)查看源碼鞋既,sendMessage中會(huì)掉一系列方法,最終走到enqueueMessage中耍铜。如下圖邑闺。


對(duì)于enqueueMessage中代碼就不分析了,現(xiàn)在我們知道消息是怎么進(jìn)入消息隊(duì)列的棕兼,又是怎么被Looper給取走陡舅,交給Handler去處理的了,整個(gè)消息機(jī)制基本就弄清楚了伴挚。其實(shí)靶衍,嚴(yán)格來(lái)說(shuō),消息可以分為系統(tǒng)消息和我們自定義的消息茎芋,自定義的消息就是發(fā)送一個(gè)Message颅眶,然后在handlerMessage中進(jìn)行處理,那么系統(tǒng)的消息呢田弥,比如四大組件的生命周期回掉是系統(tǒng)消息涛酗,跨進(jìn)程通信也是系統(tǒng)消息,應(yīng)用程序退出還是一個(gè)系統(tǒng)消息偷厦。這些消息在哪里商叹?誰(shuí)來(lái)處理呢?在ActivityThread中有一個(gè)成員final H mH = new H();這個(gè)mH就是來(lái)處理系統(tǒng)消息的只泼。

 private class H extends Handler {
        public static final int LAUNCH_ACTIVITY         = 100;
        //...
        public static final int ENTER_ANIMATION_COMPLETE = 149;

        String codeToString(int code) {
            if (DEBUG_MESSAGES) {
                switch (code) {
                    case LAUNCH_ACTIVITY: return "LAUNCH_ACTIVITY";
                      //...
                    case ENTER_ANIMATION_COMPLETE: return "ENTER_ANIMATION_COMPLETE";
                }
            }
            return Integer.toString(code);
        }
        public void handleMessage(Message msg) {
            if (DEBUG_MESSAGES) Slog.v(TAG, ">>> handling: " + codeToString(msg.what));
            switch (msg.what) {
                  //...
                case LAUNCH_ACTIVITY: {
                    Trace.traceBegin(Trace.TRACE_TAG_ACTIVITY_MANAGER, "activityStart");
                    final ActivityClientRecord r = (ActivityClientRecord) msg.obj;

                    r.packageInfo = getPackageInfoNoCheck(
                            r.activityInfo.applicationInfo, r.compatInfo);
                    handleLaunchActivity(r, null);
                    Trace.traceEnd(Trace.TRACE_TAG_ACTIVITY_MANAGER);
                } break;
            
                case PAUSE_ACTIVITY:
                    Trace.traceBegin(Trace.TRACE_TAG_ACTIVITY_MANAGER, "activityPause");
                    handlePauseActivity((IBinder)msg.obj, false, (msg.arg1&1) != 0, msg.arg2,
                            (msg.arg1&2) != 0);
                    maybeSnapshot();
                    Trace.traceEnd(Trace.TRACE_TAG_ACTIVITY_MANAGER);
                    break;
                case PAUSE_ACTIVITY_FINISHING:
                    Trace.traceBegin(Trace.TRACE_TAG_ACTIVITY_MANAGER, "activityPause");
                    handlePauseActivity((IBinder)msg.obj, true, (msg.arg1&1) != 0, msg.arg2,
                            (msg.arg1&1) != 0);
                    Trace.traceEnd(Trace.TRACE_TAG_ACTIVITY_MANAGER);
                    break;
             
                case HIDE_WINDOW:
                    Trace.traceBegin(Trace.TRACE_TAG_ACTIVITY_MANAGER, "activityHideWindow");
                    handleWindowVisibility((IBinder)msg.obj, false);
                    Trace.traceEnd(Trace.TRACE_TAG_ACTIVITY_MANAGER);
                    break;
                case RESUME_ACTIVITY:
                    Trace.traceBegin(Trace.TRACE_TAG_ACTIVITY_MANAGER, "activityResume");
                    handleResumeActivity((IBinder) msg.obj, true, msg.arg1 != 0, true);
                    Trace.traceEnd(Trace.TRACE_TAG_ACTIVITY_MANAGER);
                    break;
              
                case DESTROY_ACTIVITY:
                    Trace.traceBegin(Trace.TRACE_TAG_ACTIVITY_MANAGER, "activityDestroy");
                    handleDestroyActivity((IBinder)msg.obj, msg.arg1 != 0,
                            msg.arg2, false);
                    Trace.traceEnd(Trace.TRACE_TAG_ACTIVITY_MANAGER);
                    break;
                case CREATE_SERVICE:
                    Trace.traceBegin(Trace.TRACE_TAG_ACTIVITY_MANAGER, "serviceCreate");
                    handleCreateService((CreateServiceData)msg.obj);
                    Trace.traceEnd(Trace.TRACE_TAG_ACTIVITY_MANAGER);
                    break;
                case BIND_SERVICE:
                    Trace.traceBegin(Trace.TRACE_TAG_ACTIVITY_MANAGER, "serviceBind");
                    handleBindService((BindServiceData)msg.obj);
                    Trace.traceEnd(Trace.TRACE_TAG_ACTIVITY_MANAGER);
                    break;
                case UNBIND_SERVICE:
                    Trace.traceBegin(Trace.TRACE_TAG_ACTIVITY_MANAGER, "serviceUnbind");
                    handleUnbindService((BindServiceData)msg.obj);
                    Trace.traceEnd(Trace.TRACE_TAG_ACTIVITY_MANAGER);
                    break;
                case SERVICE_ARGS:
                    Trace.traceBegin(Trace.TRACE_TAG_ACTIVITY_MANAGER, "serviceStart");
                    handleServiceArgs((ServiceArgsData)msg.obj);
                    Trace.traceEnd(Trace.TRACE_TAG_ACTIVITY_MANAGER);
                    break;
                case STOP_SERVICE:
                    Trace.traceBegin(Trace.TRACE_TAG_ACTIVITY_MANAGER, "serviceStop");
                    handleStopService((IBinder)msg.obj);
                    maybeSnapshot();
                    Trace.traceEnd(Trace.TRACE_TAG_ACTIVITY_MANAGER);
                    break;
             
                case SUICIDE:
                    Process.killProcess(Process.myPid());
                    break;
              //...
            }
            if (DEBUG_MESSAGES) Slog.v(TAG, "<<< done: " + codeToString(msg.what));
        }
    }

看到了吧剖笙,如果要退出應(yīng)用程序,需要發(fā)送一個(gè)SUICIDE消息请唱,綁定服務(wù)需要發(fā)送一個(gè)BIND_SERVICE消息等等弥咪。

最后總結(jié)一個(gè)Android消息機(jī)制涉及到的類(lèi)以及類(lèi)的重要方法过蹂,在整個(gè)機(jī)制中它們扮演著不同的角色也承擔(dān)著各自的不同責(zé)任。

  • Thread
    負(fù)責(zé)業(yè)務(wù)邏輯的實(shí)施酪夷。
    線(xiàn)程中的操作是由各自的業(yè)務(wù)邏輯所決定的榴啸,視具體情況進(jìn)行孽惰。

  • Handler
    負(fù)責(zé)發(fā)送消息和處理消息晚岭。
    通常的做法是在主線(xiàn)程中建立Handler并利用它在子線(xiàn)程中向主線(xiàn)程發(fā)送消息,在主線(xiàn)程接收到消息后會(huì)對(duì)其進(jìn)行處理

  • MessageQueue
    負(fù)責(zé)保存消息勋功。
    Handler發(fā)出的消息均會(huì)被保存到消息隊(duì)列MessageQueue中坦报,系統(tǒng)會(huì)根據(jù)Message距離觸發(fā)時(shí)間的長(zhǎng)短決定該消息在隊(duì)列中位置。在隊(duì)列中的消息會(huì)依次出隊(duì)得到相應(yīng)的處理狂鞋。

  • Looper
    負(fù)責(zé)輪詢(xún)消息隊(duì)列片择。
    Looper使用其loop()方法一直輪詢(xún)消息隊(duì)列,并在消息出隊(duì)時(shí)將其派發(fā)至對(duì)應(yīng)的Handler.

  • ThreadLocal
    將線(xiàn)程和Looper相關(guān)聯(lián)

Please accept mybest wishes for your happiness and success

參考資料:http://blog.csdn.net/lfdfhl/article/details/53332936

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末骚揍,一起剝皮案震驚了整個(gè)濱河市字管,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌信不,老刑警劉巖嘲叔,帶你破解...
    沈念sama閱讀 216,544評(píng)論 6 501
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場(chǎng)離奇詭異抽活,居然都是意外死亡硫戈,警方通過(guò)查閱死者的電腦和手機(jī),發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 92,430評(píng)論 3 392
  • 文/潘曉璐 我一進(jìn)店門(mén)下硕,熙熙樓的掌柜王于貴愁眉苦臉地迎上來(lái)丁逝,“玉大人,你說(shuō)我怎么就攤上這事梭姓∷祝” “怎么了?”我有些...
    開(kāi)封第一講書(shū)人閱讀 162,764評(píng)論 0 353
  • 文/不壞的土叔 我叫張陵誉尖,是天一觀的道長(zhǎng)罪既。 經(jīng)常有香客問(wèn)我,道長(zhǎng)释牺,這世上最難降的妖魔是什么萝衩? 我笑而不...
    開(kāi)封第一講書(shū)人閱讀 58,193評(píng)論 1 292
  • 正文 為了忘掉前任,我火速辦了婚禮没咙,結(jié)果婚禮上猩谊,老公的妹妹穿的比我還像新娘。我一直安慰自己祭刚,他們只是感情好牌捷,可當(dāng)我...
    茶點(diǎn)故事閱讀 67,216評(píng)論 6 388
  • 文/花漫 我一把揭開(kāi)白布墙牌。 她就那樣靜靜地躺著,像睡著了一般暗甥。 火紅的嫁衣襯著肌膚如雪喜滨。 梳的紋絲不亂的頭發(fā)上,一...
    開(kāi)封第一講書(shū)人閱讀 51,182評(píng)論 1 299
  • 那天撤防,我揣著相機(jī)與錄音虽风,去河邊找鬼。 笑死寄月,一個(gè)胖子當(dāng)著我的面吹牛辜膝,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播漾肮,決...
    沈念sama閱讀 40,063評(píng)論 3 418
  • 文/蒼蘭香墨 我猛地睜開(kāi)眼厂抖,長(zhǎng)吁一口氣:“原來(lái)是場(chǎng)噩夢(mèng)啊……” “哼!你這毒婦竟也來(lái)了克懊?” 一聲冷哼從身側(cè)響起忱辅,我...
    開(kāi)封第一講書(shū)人閱讀 38,917評(píng)論 0 274
  • 序言:老撾萬(wàn)榮一對(duì)情侶失蹤,失蹤者是張志新(化名)和其女友劉穎谭溉,沒(méi)想到半個(gè)月后墙懂,有當(dāng)?shù)厝嗽跇?shù)林里發(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 45,329評(píng)論 1 310
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡夜只,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 37,543評(píng)論 2 332
  • 正文 我和宋清朗相戀三年燕差,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了板丽。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片旺入。...
    茶點(diǎn)故事閱讀 39,722評(píng)論 1 348
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡苞氮,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出旅挤,到底是詐尸還是另有隱情踢关,我是刑警寧澤,帶...
    沈念sama閱讀 35,425評(píng)論 5 343
  • 正文 年R本政府宣布粘茄,位于F島的核電站签舞,受9級(jí)特大地震影響,放射性物質(zhì)發(fā)生泄漏柒瓣。R本人自食惡果不足惜儒搭,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,019評(píng)論 3 326
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望芙贫。 院中可真熱鬧搂鲫,春花似錦、人聲如沸磺平。這莊子的主人今日做“春日...
    開(kāi)封第一講書(shū)人閱讀 31,671評(píng)論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)。三九已至擦酌,卻和暖如春俱诸,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背赊舶。 一陣腳步聲響...
    開(kāi)封第一講書(shū)人閱讀 32,825評(píng)論 1 269
  • 我被黑心中介騙來(lái)泰國(guó)打工睁搭, 沒(méi)想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留,地道東北人锯岖。 一個(gè)月前我還...
    沈念sama閱讀 47,729評(píng)論 2 368
  • 正文 我出身青樓介袜,卻偏偏與公主長(zhǎng)得像,于是被迫代替她去往敵國(guó)和親出吹。 傳聞我的和親對(duì)象是個(gè)殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 44,614評(píng)論 2 353

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