Android Handler機制7之消息發送

Android Handler機制系列文章整體內容如下:

本片文章的主要內容如下:

  • 1、Handler發送消息
  • 2、Handler的send方案
  • 3、Handler的post方案

一 、Handler發送消息

大家平時發送消息主要是調用的兩大類方法
如下兩圖:


send方案.png
send方案.png

光看上面這些API你可能會覺得handler能法兩種消息,一種是Runnable對象,一種是message對象,這是直觀的理解,但其實post發出的Runnable對象最后都封裝成message對象了。

  • send方案發送消息(需要回調才能接收消息)
    • 1、sendMessage(Message) 立即發送Message到消息隊列
    • 2、sendMessageAtFrontOfQueue(Message) 立即發送Message到隊列,而且是放在隊列的最前面
    • 3、sendMessageAtTime(Message,long) 設置時間,發送Message到隊列
    • 4、sendMessageDelayed(Message,long) 延時若干毫秒后,發送Message到隊列
  • post方案 立即發送Message到消息隊列
    • 1、post(Runnable) 立即發送Message到消息隊列
    • 2、postAtFrontOfQueue(Runnable) 立即發送Message到隊列,而且是放在隊列的最前面
    • 3、postAtTime(Runnable,long) 設置時間,發送Message到隊列
    • 4、postDelayed(Runnable,long) 在延時若干毫秒后,發送Message到隊列

下面我們就先從send方案中的第一個sendMessage() 開始源碼跟蹤下:

二、 Handler的send方案

我們以Handler的sendMessage(Message msg)為例子。

(一)、boolean sendMessage(Message msg)方法

代碼在Handler.java 505行

    /**
     * Pushes a message onto the end of the message queue after all pending messages
     * before the current time. It will be received in {@link #handleMessage},
     * in the thread attached to this handler.
     *  
     * @return Returns true if the message was successfully placed in to the 
     *         message queue.  Returns false on failure, usually because the
     *         looper processing the message queue is exiting.
     */
    public final boolean sendMessage(Message msg)
    {
        return sendMessageDelayed(msg, 0);
    }

老規矩先翻譯一下注釋:

在當前時間,在所有待處理消息之后,將消息推送到消息隊列的末尾。在和當前線程關聯的的Handler里面的handleMessage將收到這條消息,

我們看到sendMessage(Message)里面代碼很簡單,就是調用了sendMessageDelayed(msg,0)

1、boolean sendMessageDelayed(Message msg, long delayMillis)

代碼在Handler.java 565行

    /**
     * Enqueue a message into the message queue after all pending messages
     * before (current time + delayMillis). You will receive it in
     * {@link #handleMessage}, in the thread attached to this handler.
     *  
     * @return Returns true if the message was successfully placed in to the 
     *         message queue.  Returns false on failure, usually because the
     *         looper processing the message queue is exiting.  Note that a
     *         result of true does not mean the message will be processed -- if
     *         the looper is quit before the delivery time of the message
     *         occurs then the message will be dropped.
     */
    public final boolean sendMessageDelayed(Message msg, long delayMillis)
    {
        if (delayMillis < 0) {
            delayMillis = 0;
        }
        return sendMessageAtTime(msg, SystemClock.uptimeMillis() + delayMillis);
    }

注釋和boolean sendMessage(Message msg)方法差不多,我就不翻譯了

該方法內部就做了兩件事

  • 1、判斷delayMillis是否小于0
  • 2、調用了public boolean sendMessageAtTime(Message msg, long uptimeMillis)方法
2、boolean sendMessageAtTime(Message msg, long uptimeMillis)

代碼在Handler.java 592行

    /**
     * Enqueue a message into the message queue after all pending messages
     * before the absolute time (in milliseconds) <var>uptimeMillis</var>.
     * <b>The time-base is {@link android.os.SystemClock#uptimeMillis}.</b>
     * Time spent in deep sleep will add an additional delay to execution.
     * You will receive it in {@link #handleMessage}, in the thread attached
     * to this handler.
     * 
     * @param uptimeMillis The absolute time at which the message should be
     *         delivered, using the
     *         {@link android.os.SystemClock#uptimeMillis} time-base.
     *         
     * @return Returns true if the message was successfully placed in to the 
     *         message queue.  Returns false on failure, usually because the
     *         looper processing the message queue is exiting.  Note that a
     *         result of true does not mean the message will be processed -- if
     *         the looper is quit before the delivery time of the message
     *         occurs then the message will be dropped.
     */
    public boolean sendMessageAtTime(Message msg, long uptimeMillis) {
        MessageQueue queue = mQueue;
        if (queue == null) {
            RuntimeException e = new RuntimeException(
                    this + " sendMessageAtTime() called with no mQueue");
            Log.w("Looper", e.getMessage(), e);
            return false;
        }
        return enqueueMessage(queue, msg, uptimeMillis);
    }

老規矩先翻譯一下注釋:

以android系統的SystemClock的uptimeMillis()為基準,以毫秒為基本單位的絕對時間下,在所有待處理消息后,將消息放到消息隊列中。深度睡眠中的時間將會延遲執行的時間,你將在和當前線程辦的規定的Handler中的handleMessage中收到該消息。

這里順便提一下異步的作用,因為通常我們理解的異步是指新開一個線程,但是這里不是,因為異步的也是發送到looper所綁定的消息隊列中,這里的異步主要是針對Message中的障柵(Barrier)而言的,當出現障柵(Barrier)的時候,同步的會被阻塞,而異步的則不會。所以這個異步僅僅是一個標記而已。

該方法內部就做了兩件事

  • 1、獲取消息隊列,并對該消息隊列做非空判斷,如果為null,直接返回false,表示消息發送失敗
  • 2、調用了boolean enqueueMessage(MessageQueue queue, Message msg, long uptimeMillis)方法
3、boolean enqueueMessage(MessageQueue queue, Message msg, long uptimeMillis)

代碼在Handler.java 626行

    private boolean enqueueMessage(MessageQueue queue, Message msg, long uptimeMillis) {
        msg.target = this;
        if (mAsynchronous) {
            msg.setAsynchronous(true);
        }
        return queue.enqueueMessage(msg, uptimeMillis);
    }

本方法內部做了三件事

  • 1、設置msg的target變量,并將target指向自己
  • 2、如果Handler的mAsynchronous值為true(默認為false,即不設置),則設置msg的flags值,讓是否異步在Handler和Message達成統一。
  • 3、調用MessageQueue的enqueueMessage()方法
4、boolean enqueueMessage(Message msg, long when)方法

代碼在MessageQueue.java 533行

    boolean enqueueMessage(Message msg, long when) {
        //第一步
        if (msg.target == null) {
            throw new IllegalArgumentException("Message must have a target.");
        }
        // 第二步
        if (msg.isInUse()) {
            throw new IllegalStateException(msg + " This message is already in use.");
        }
        // 第三步
        synchronized (this) {
             // 第四步
            //判斷消息隊列是否正在關閉
            if (mQuitting) {
                IllegalStateException e = new IllegalStateException(
                        msg.target + " sending message to a Handler on a dead thread");
                Log.w(TAG, e.getMessage(), e);
                msg.recycle();
                return false;
            }
             // 第五步
            msg.markInUse();
            msg.when = when;
            Message p = mMessages;
            boolean needWake;
             // 第六步
            //根據when的比較來判斷要添加的Message是否應該放在隊列頭部,當第一個添加消息的時候,
            // 測試隊列為空,所以該Message也應該位于頭部。
            if (p == null || when == 0 || when < p.when) {
                // New head, wake up the event queue if blocked.
                // 把msg的下一個元素設置為p
                msg.next = p;
                // 把msg設置為鏈表的頭部元素
                mMessages = msg;
                 // 如果有阻塞,則需要喚醒
                needWake = mBlocked;
            } else {
                 // 第七步
                // Inserted within the middle of the queue.  Usually we don't have to wake
                // up the event queue unless there is a barrier at the head of the queue
                // and the message is the earliest asynchronous message in the queue.
                //除非消息隊列的頭部是障柵(barrier),或者消息隊列的第一個消息是異步消息,
                //否則如果是插入到中間位置,我們通常不喚醒消息隊列,
                 // 第八步
                needWake = mBlocked && p.target == null && msg.isAsynchronous();
                Message prev;
                  // 第九步
                 // 不斷遍歷消息隊列,根據when的比較找到合適的插入Message的位置。
                for (;;) {
                    prev = p;
                    p = p.next;
                    // 第十步
                    if (p == null || when < p.when) {
                        break;
                    }
                    // 第十一 步
                    if (needWake && p.isAsynchronous()) {
                        needWake = false;
                    }
                }
                // 第十二步
                msg.next = p; // invariant: p == prev.next
                prev.next = msg;
            }
              // 第十三步
            // We can assume mPtr != 0 because mQuitting is false.
            if (needWake) {
                nativeWake(mPtr);
            }
        }
         // 第十四步
        return true;
    }

因為這是消息入隊的流程,為了讓大家更好的理解,我將上面的流程大致分為12步驟

  • 第1步驟、 判斷msg的target變量是否為null,如果為null,則為障柵(barrier),而障柵(barrier)入隊則是通過postSyncBarrier()方法入隊,所以msg的target一定有值
  • 第2步驟、 判斷msg的標志位,因為此時的msg應該是要入隊,意味著msg的標志位應該顯示還未被使用。如果顯示已使用,明顯有問題,直接拋異常。
  • 第3步驟、 加入同步鎖。
  • 第4步驟、 判斷消息隊列是否正在被關閉,如果是正在被關閉,則return false告訴消息入隊是失敗,并且回收消息
  • 第5步驟、 設置msg的when并且修改msg的標志位,msg標志位顯示為已使用
  • 第6步驟、 如果p==null則說明消息隊列中的鏈表的頭部元素為null;when == 0 表示立即執行;when< p.when 表示 msg的執行時間早與鏈表中的頭部元素的時間,所以上面三個條件,那個條件成立,都要把msg設置成消息隊列中鏈表的頭部是元素
  • 第7步驟、 如果上面三個條件都不滿足則說明要把msg插入到中間的位置,不需要插入到頭部
  • 第8步驟、 如果頭部元素不是障柵(barrier)或者異步消息,而且還是插入中間的位置,我們是不喚醒消息隊列的。
  • 第9步驟、 進入一個死循環,將p的值賦值給prev,前面的帶我們知道,p指向的是mMessage,所以這里是將prev指向了mMessage,在下一次循環的時候,prev則指向了第一個message,一次類推。接著講p指向了p.next也就是mMessage.next,也就是消息隊列鏈表中的第二個元素。這一步驟實現了消息指針的移動,此時p表示的消息隊列中第二個元素。
  • 第10步驟、 p==null,則說明沒有下一個元素,即消息隊列到頭了,跳出循環;p!=null&&when < p.when 則說明當前需要入隊的這個message的執行時間是小于隊列中這個任務的執行時間的,也就是說這個需要入隊的message需要比隊列中這個message先執行,則說明這個位置剛剛是適合這個message的,所以跳出循環。 如果上面的兩個條件都不滿足,則說明這個位置還不是放置這個需要入隊的message,則繼續跟鏈表中后面的元素,也就是繼續跟消息隊列中的下一個消息進行對比,直到滿足條件或者到達隊列的末尾。
  • 第11步驟、 因為沒有滿足條件,說明隊列中還有消息,不需要喚醒。
  • 第12步驟、 跳出循環后主要做了兩件事:事件A,將入隊的這個消息的next指向循環中獲取到的應該排在這個消息之后message。事件B,將msg前面的message.next指向了msg。這樣就將一個message完成了入隊。
  • 第13步驟、 如果需要喚醒,則喚醒,具體請看后面的Handler中的Native詳解。
  • 第14步驟、 返回true,告知入隊成功。

提供兩張圖,讓大家更好的理解入隊


入隊1.png
入隊2.png

總結一句話就是:就是遍歷消息隊列中所有的消息,根據when的比較找到合適添加Message的位置。

上面是sendMessage(Message msg)發送消息機制,這樣再來看下其他的send方案

(二) boolean sendMessageAtFrontOfQueue(Message msg)

代碼在Handler.java 615行

    /**
     * Enqueue a message at the front of the message queue, to be processed on
     * the next iteration of the message loop.  You will receive it in
     * {@link #handleMessage}, in the thread attached to this handler.
     * <b>This method is only for use in very special circumstances -- it
     * can easily starve the message queue, cause ordering problems, or have
     * other unexpected side-effects.</b>
     *  
     * @return Returns true if the message was successfully placed in to the 
     *         message queue.  Returns false on failure, usually because the
     *         looper processing the message queue is exiting.
     */
    public final boolean sendMessageAtFrontOfQueue(Message msg) {
        MessageQueue queue = mQueue;
        if (queue == null) {
            RuntimeException e = new RuntimeException(
                this + " sendMessageAtTime() called with no mQueue");
            Log.w("Looper", e.getMessage(), e);
            return false;
        }
        return enqueueMessage(queue, msg, 0);
    }

老規矩先翻譯一下注釋:

在消息隊列的最前面插入一個消息,在消息循環的下一次迭代中進行處理。你將在當前線程關聯的Handler的handleMessage()中收到這個消息。由于它可以輕松的解決消息隊列的排序問題和其他的意外副作用。

方法內部的實現和boolean sendMessageAtTime(Message msg, long uptimeMillis)大體上一致,唯一的區別就是該方法在調用enqueueMessage(MessageQueue, Message, long)方法的時候,最后一個參數是0而已。

(三) boolean sendEmptyMessage(int what)

代碼在Handler.java 517行

   /**
     * Sends a Message containing only the what value.
     *  
     * @return Returns true if the message was successfully placed in to the 
     *         message queue.  Returns false on failure, usually because the
     *         looper processing the message queue is exiting.
     */
    public final boolean sendEmptyMessage(int what)
    {
        return sendEmptyMessageDelayed(what, 0);
    }

老規矩先翻譯一下注釋:

發送一個僅有what的Message

內容很簡單,也就是調用sendEmptyMessageDelayed(int,long)而已,那么下面我們來看下sendEmptyMessageDelayed(int,long)的具體實現。

1、boolean sendEmptyMessageDelayed(int what, long delayMillis)

代碼在Handler.java 531行

    /**
     * Sends a Message containing only the what value, to be delivered
     * after the specified amount of time elapses.
     * @see #sendMessageDelayed(android.os.Message, long) 
     * 
     * @return Returns true if the message was successfully placed in to the 
     *         message queue.  Returns false on failure, usually because the
     *         looper processing the message queue is exiting.
     */
    public final boolean sendEmptyMessageDelayed(int what, long delayMillis) {
        Message msg = Message.obtain();
        msg.what = what;
        return sendMessageDelayed(msg, delayMillis);
    }

老規矩先翻譯一下注釋:

發送一個僅有what的Message,并且延遲特定的時間發送

這個方法內部主要就是做了3件事

  • 1、調用Message.obtain();從消息對象池中獲取一個空的Message。
  • 2、設置這個Message的what值
  • 3、調用sendMessageDelayed(Message,long) 將這個消息方法

sendMessageDelayed(Message,long) 這個方法上面有講解過,這里就不詳細說了

(四) boolean sendEmptyMessageAtTime(int what, long uptimeMillis)

代碼在Handler.java 547行

    /**
     * Sends a Message containing only the what value, to be delivered 
     * at a specific time.
     * @see #sendMessageAtTime(android.os.Message, long)
     *  
     * @return Returns true if the message was successfully placed in to the 
     *         message queue.  Returns false on failure, usually because the
     *         looper processing the message queue is exiting.
     */

    public final boolean sendEmptyMessageAtTime(int what, long uptimeMillis) {
        Message msg = Message.obtain();
        msg.what = what;
        return sendMessageAtTime(msg, uptimeMillis);
    }

老規矩先翻譯一下注釋:

發送一個僅有what的Message,并且在特定的時間發送

這個方法內部主要就是做了3件事

  • 1、調用Message.obtain();從消息對象池中獲取一個空的Message。
  • 2、設置這個Message的what值
  • 3、調用sendMessageAtTime(Message,long) 將這個消息方法

(五) 小結

綜上所述

  • public final boolean sendMessage(Message msg)
  • public final boolean sendEmptyMessage(int what)
  • public final boolean sendEmptyMessageDelayed(int what, long delayMillis)
  • public final boolean sendEmptyMessageAtTime(int what, long uptimeMillis)
  • public final boolean sendMessageDelayed(Message msg, long delayMillis)
  • public boolean sendMessageAtTime(Message msg, long uptimeMillis)
  • public boolean sendMessageAtTime(Message msg, long uptimeMillis)
  • public final boolean sendMessageAtFrontOfQueue(Message msg)

以上這些send方案都會從這里或者那里最終走到boolean enqueueMessage(MessageQueue queue, Message msg, long uptimeMillis)如下圖

send小結.png

三、 Handler的post方案

(一)、boolean post(Runnable r)方法

代碼在Handler.java 324行

    /**
     * Causes the Runnable r to be added to the message queue.
     * The runnable will be run on the thread to which this handler is 
     * attached. 
     *  
     * @param r The Runnable that will be executed.
     * 
     * @return Returns true if the Runnable was successfully placed in to the 
     *         message queue.  Returns false on failure, usually because the
     *         looper processing the message queue is exiting.
     */
    public final boolean post(Runnable r)
    {
       return  sendMessageDelayed(getPostMessage(r), 0);
    }

老規矩先翻譯一下注釋:

將一個Runnable添加到消息隊列中,這個runnable將會在和當前Handler關聯的線程中被執行。

這個方法內部很簡單,就是調用了sendMessageDelayed(Message, long);這個方法,所以可見boolean post(Runnable r)這個方法最終還是走到上面說到的send的流程中,最終調用boolean enqueueMessage(MessageQueue queue, Message msg, long uptimeMillis)

這里面調用了Message getPostMessage(Runnable r),我們來看一下。

1、Message getPostMessage(Runnable r)方法

代碼在Handler.java 725行

    private static Message getPostMessage(Runnable r) {
        Message m = Message.obtain();
        m.callback = r;
        return m;
    }

代碼很簡單,主要是做了兩件事

  • 通過Message.obtain()從消息對象池中獲取一個空的Message
  • 將這空的Message的callback變量指向Runnable

最后返回這個Message m。

2、小結

所以我們知道boolean post(Runnable r)方法的內置也是通過Message.obtain()來獲取一個Message對象m,然后僅僅把m的callback指向參數r而已。最后最終通過調用send方案的某個流程最終調用到boolean enqueueMessage(MessageQueue queue, Message msg, long uptimeMillis)

這時候聰明的同學一定會想,那么post方案的其他方法是不是也是這樣的?是的,的確都是這樣,這都是“套路”,那我們就用一一去檢驗。

(二)、boolean postAtTime(Runnable r, long uptimeMillis)方法

代碼在Handler.java 347行

    /**
     * Causes the Runnable r to be added to the message queue, to be run
     * at a specific time given by <var>uptimeMillis</var>.
     * <b>The time-base is {@link android.os.SystemClock#uptimeMillis}.</b>
     * Time spent in deep sleep will add an additional delay to execution.
     * The runnable will be run on the thread to which this handler is attached.
     *
     * @param r The Runnable that will be executed.
     * @param uptimeMillis The absolute time at which the callback should run,
     *         using the {@link android.os.SystemClock#uptimeMillis} time-base.
     *  
     * @return Returns true if the Runnable was successfully placed in to the 
     *         message queue.  Returns false on failure, usually because the
     *         looper processing the message queue is exiting.  Note that a
     *         result of true does not mean the Runnable will be processed -- if
     *         the looper is quit before the delivery time of the message
     *         occurs then the message will be dropped.
     */
    public final boolean postAtTime(Runnable r, long uptimeMillis)
    {
        return sendMessageAtTime(getPostMessage(r), uptimeMillis);
    }

老規矩先翻譯一下注釋:

將一個Runnable添加到消息隊列中,這個runnable將會一個特定的時間被執行,這個時間是以android.os.SystemClock.uptimeMillis()為基準。如果在深度睡眠下,會推遲執行的時間,這個Runnable將會在和當前Hander關聯的線程中被執行。

方法內部也是先是調用getPostMessage(Runnable)來獲取一個Message,這個Message的callback字段指向了這個Runnable,然后調用sendMessageAtTime(Message,long)。

(三)、boolean postAtTime(Runnable r, Object token, long uptimeMillis)方法

代碼在Handler.java 372行

    /**
     * Causes the Runnable r to be added to the message queue, to be run
     * at a specific time given by <var>uptimeMillis</var>.
     * <b>The time-base is {@link android.os.SystemClock#uptimeMillis}.</b>
     * Time spent in deep sleep will add an additional delay to execution.
     * The runnable will be run on the thread to which this handler is attached.
     *
     * @param r The Runnable that will be executed.
     * @param uptimeMillis The absolute time at which the callback should run,
     *         using the {@link android.os.SystemClock#uptimeMillis} time-base.
     * 
     * @return Returns true if the Runnable was successfully placed in to the 
     *         message queue.  Returns false on failure, usually because the
     *         looper processing the message queue is exiting.  Note that a
     *         result of true does not mean the Runnable will be processed -- if
     *         the looper is quit before the delivery time of the message
     *         occurs then the message will be dropped.
     *         
     * @see android.os.SystemClock#uptimeMillis
     */
    public final boolean postAtTime(Runnable r, Object token, long uptimeMillis)
    {
        return sendMessageAtTime(getPostMessage(r, token), uptimeMillis);
    }

這個方法的翻譯同上,這個方法和上個方法唯一不同就是多了Object參數,而這個參數僅僅是把Message.obtain();獲取的Message的obj字段的指向第二個入參token而已。最后也是調用sendMessageAtTime(Message,long)。

(四)、boolean postDelayed(Runnable r, long delayMillis)方法

代碼在Handler.java 396行

    /**
     * Causes the Runnable r to be added to the message queue, to be run
     * after the specified amount of time elapses.
     * The runnable will be run on the thread to which this handler
     * is attached.
     * <b>The time-base is {@link android.os.SystemClock#uptimeMillis}.</b>
     * Time spent in deep sleep will add an additional delay to execution.
     *  
     * @param r The Runnable that will be executed.
     * @param delayMillis The delay (in milliseconds) until the Runnable
     *        will be executed.
     *        
     * @return Returns true if the Runnable was successfully placed in to the 
     *         message queue.  Returns false on failure, usually because the
     *         looper processing the message queue is exiting.  Note that a
     *         result of true does not mean the Runnable will be processed --
     *         if the looper is quit before the delivery time of the message
     *         occurs then the message will be dropped.
     */
    public final boolean postDelayed(Runnable r, long delayMillis)
    {
        return sendMessageDelayed(getPostMessage(r), delayMillis);
    }

這個方法也很簡單,就是依舊是通過getPostMessage(Runnable)來獲取一個Message,最后調用sendMessageDelayed(Message,long)而已。

(五)、boolean postDelayed(Runnable r, long delayMillis)方法

代碼在Handler.java 416行

    /**
     * Posts a message to an object that implements Runnable.
     * Causes the Runnable r to executed on the next iteration through the
     * message queue. The runnable will be run on the thread to which this
     * handler is attached.
     * <b>This method is only for use in very special circumstances -- it
     * can easily starve the message queue, cause ordering problems, or have
     * other unexpected side-effects.</b>
     *  
     * @param r The Runnable that will be executed.
     * 
     * @return Returns true if the message was successfully placed in to the 
     *         message queue.  Returns false on failure, usually because the
     *         looper processing the message queue is exiting.
     */
    public final boolean postAtFrontOfQueue(Runnable r)
    {
        return sendMessageAtFrontOfQueue(getPostMessage(r));
    }

這個方法也很簡單,就是依舊是通過getPostMessage(Runnable)來獲取一個Message,最后調用sendMessageAtFrontOfQueue(Message)而已。

(六)、小結

Handler的post方案的如下方法

  • boolean post(Runnable r)
  • postAtTime(Runnable r, long uptimeMillis)
  • boolean postAtTime(Runnable r, Object token, long uptimeMillis)
  • boolean postDelayed(Runnable r, long delayMillis)
  • boolean postAtFrontOfQueue(Runnable r)
    都是通過Message getPostMessage(Runnable )中調用Message m = Message.obtain();來獲取一個空的Message,然后把這個Message的callback變量指向了Runnable,最終調用相應的send方案而已。

所以我們可以這樣說:

Handler的發送消息(障柵除外),無論是通過send方案還是pos方案最終都會做走到 boolean enqueueMessage(MessageQueue queue, Message msg, long uptimeMillis)
消息發送.png
最后編輯于
?著作權歸作者所有,轉載或內容合作請聯系作者
平臺聲明:文章內容(如有圖片或視頻亦包括在內)由作者上傳并發布,文章內容僅代表作者本人觀點,簡書系信息發布平臺,僅提供信息存儲服務。
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市,隨后出現的幾起案子,更是在濱河造成了極大的恐慌,老刑警劉巖,帶你破解...
    沈念sama閱讀 230,825評論 6 546
  • 序言:濱河連續發生了三起死亡事件,死亡現場離奇詭異,居然都是意外死亡,警方通過查閱死者的電腦和手機,發現死者居然都...
    沈念sama閱讀 99,814評論 3 429
  • 文/潘曉璐 我一進店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人,你說我怎么就攤上這事。” “怎么了?”我有些...
    開封第一講書人閱讀 178,980評論 0 384
  • 文/不壞的土叔 我叫張陵,是天一觀的道長。 經常有香客問我,道長,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 64,064評論 1 319
  • 正文 為了忘掉前任,我火速辦了婚禮,結果婚禮上,老公的妹妹穿的比我還像新娘。我一直安慰自己,他們只是感情好,可當我...
    茶點故事閱讀 72,779評論 6 414
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著,像睡著了一般。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發上,一...
    開封第一講書人閱讀 56,109評論 1 330
  • 那天,我揣著相機與錄音,去河邊找鬼。 笑死,一個胖子當著我的面吹牛,可吹牛的內容都是我干的。 我是一名探鬼主播,決...
    沈念sama閱讀 44,099評論 3 450
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了?” 一聲冷哼從身側響起,我...
    開封第一講書人閱讀 43,287評論 0 291
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后,有當地人在樹林里發現了一具尸體,經...
    沈念sama閱讀 49,799評論 1 338
  • 正文 獨居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內容為張勛視角 年9月15日...
    茶點故事閱讀 41,515評論 3 361
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發現自己被綠了。 大學時的朋友給我發了我未婚夫和他白月光在一起吃飯的照片。...
    茶點故事閱讀 43,750評論 1 375
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖,靈堂內的尸體忽然破棺而出,到底是詐尸還是另有隱情,我是刑警寧澤,帶...
    沈念sama閱讀 39,221評論 5 365
  • 正文 年R本政府宣布,位于F島的核電站,受9級特大地震影響,放射性物質發生泄漏。R本人自食惡果不足惜,卻給世界環境...
    茶點故事閱讀 44,933評論 3 351
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧,春花似錦、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 35,327評論 0 28
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至,卻和暖如春,著一層夾襖步出監牢的瞬間,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 36,667評論 1 296
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留,地道東北人。 一個月前我還...
    沈念sama閱讀 52,492評論 3 400
  • 正文 我出身青樓,卻偏偏與公主長得像,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當晚...
    茶點故事閱讀 48,703評論 2 380

推薦閱讀更多精彩內容