Android 9.0 源碼_機(jī)制篇 -- 全面解析 Handler 機(jī)制(原理篇)下

開篇


上一篇說到曹操敗走華容道后...咳咳兆沙,不好意思走錯(cuò)片場了候引。需要看上篇的同學(xué)出門左拐點(diǎn)主頁觀看

8.quit()

 public void quit() {
        mQueue.quit(false);          // 消息移除
    }

    public void quitSafely() {
        mQueue.quit(true);           // 安全消息移除
    }

Looper.quit() 方法的實(shí)現(xiàn)最終調(diào)用的是 MessageQueue.quit() 方法码倦。

9.MessageQueue.quit()

void quit(boolean safe) {
        if (!mQuitAllowed) {    當(dāng) mQuitAllowed 為 false址儒,表示不運(yùn)行退出捕发,強(qiáng)行調(diào)用 quit() 會(huì)拋出異常
            throw new IllegalStateException("Main thread not allowed to quit.");
        }

        synchronized (this) {
            if (mQuitting) {
                return;
            }
            mQuitting = true;

            if (safe) {
                removeAllFutureMessagesLocked();
            } else {
                removeAllMessagesLocked();
            }

            // We can assume mPtr != 0 because mQuitting was previously false.
            nativeWake(mPtr);
        }
    }

消息退出的方式:

######1.當(dāng) safe = true 時(shí)庙楚,只移除尚未觸發(fā)的所有消息面哥,對于正在觸發(fā)的消息并不移除
######2.當(dāng) safe = flase 時(shí)哎壳,移除所有的消息

#三Handler
-------------------------------------------------------------
##構(gòu)造函數(shù)
-------------------------------------------------------------
### 無參構(gòu)造
public Handler() {
    this(null, false);
}

public Handler(Callback callback) {
    this(callback, false);
}

public Handler(boolean async) {
    this(null, async);
}

public Handler(Callback callback, boolean async) {
    // 匿名類、內(nèi)部類或本地類都必須申明為static幢竹,否則會(huì)警告可能出現(xiàn)內(nèi)存泄露
    if (FIND_POTENTIAL_LEAKS) {     // 默認(rèn)為 false
        final Class<? extends Handler> klass = getClass();
        if ((klass.isAnonymousClass() || klass.isMemberClass() || klass.isLocalClass()) &&
                (klass.getModifiers() & Modifier.STATIC) == 0) {
            Log.w(TAG, "The following Handler class should be static or leaks might occur: " +
                klass.getCanonicalName());
        }
    }
對于 Handler 的無參構(gòu)造方法耳峦,默認(rèn)采用當(dāng)前線程 TLS 中的 Looper 對象,并且 callback 回調(diào)方法為 null焕毫,且消息為同步處理方式蹲坷。只要執(zhí)行的 Looper.prepare() 方法驶乾,那么便可以獲取有效的 Looper 對象。

1.dispatchMessage()


在 Looper.loop() 中循签,當(dāng)發(fā)現(xiàn)有消息時(shí)级乐,調(diào)用消息的目標(biāo) handler,執(zhí)行 dispatchMessage() 方法來分發(fā)消息县匠。

   /**
     * Handle system messages here.
     */
    public void dispatchMessage(Message msg) {
        if (msg.callback != null) {
            // 當(dāng) Message 存在回調(diào)方法风科,回調(diào) msg.callback.run() 方法
            handleCallback(msg);
        } else {
            if (mCallback != null) {
                // 當(dāng) Handler 存在 Callback 成員變量時(shí),回調(diào)方法 handleMessage()
                if (mCallback.handleMessage(msg)) {
                    return;
                }
            }
            // Handler 自身的回調(diào)方法 handleMessage()
            handleMessage(msg);
        }
    }

我們需要重點(diǎn)分析下這個(gè)函數(shù):

首先會(huì)判斷 msg.callback 存不存在乞旦,msg.callback 是 Runnable 類型贼穆,如果 msg.callback 存在,那么說明該 Message 是通過執(zhí)行 Handler 的 post() 系列方法將 Message 放入到消息隊(duì)列中的兰粉,這種情況下會(huì)執(zhí)行 handleCallback(msg)故痊。

2.handleCallback

源碼如下:

    private static void handleCallback(Message message) {
        message.callback.run();
    }

這樣我們就清楚地看到我們執(zhí)行了 msg.callback 的 run 方法,也就是執(zhí)行了 post() 所傳遞的 Runnable 對象的 run 方法玖姑。

3.mCallback

如果我們不是通過 post() 系列方法將 Message 放入到消息隊(duì)列中的愕秫,那么 msg.callback 就是 null ,代碼繼續(xù)往下執(zhí)行焰络。

接著我們會(huì)判斷 Handler 的成員字段 mCallback 存不存在戴甩。mCallback 是 Hanlder.Callback 類型的,我們在上面提到過闪彼,在 Handler 的構(gòu)造函數(shù)中我們可以傳遞 Hanlder.Callback 類型的對象甜孤,該對象需要實(shí)現(xiàn) handleMessage 方法,如果我們在構(gòu)造函數(shù)中傳遞了該 Callback 對象备蚓,那么我們就會(huì)讓 Callback 的 handleMessage 方法來處理 Message课蔬。

    final Callback mCallback;
    
    public interface Callback {
        /**
         * @param msg A {@link android.os.Message Message} object
         * @return True if no further handling is desired
         */
        public boolean handleMessage(Message msg);
    }
如果我們在構(gòu)造函數(shù)中沒有傳入 Callback 類型的對象,那么 mCallback 就為 null 郊尝,那么我們會(huì)調(diào)用 Handler 自身的 hanldeMessage 方法二跋,該方法默認(rèn)是個(gè)空方法,我們需要自己重寫實(shí)現(xiàn)該方法流昏。
    /**
     * Subclasses must implement this to receive messages.
     */
    public void handleMessage(Message msg) {      // 空函數(shù)
    }

綜上所述扎即,我們可以看到 Handler 提供了三種途徑處理 Message ,而且處理有前后優(yōu)先級(jí)之分:首先嘗試讓 post() 中傳遞的 Runnable 執(zhí)行况凉,其次嘗試讓 Handler 構(gòu)造函數(shù)中傳入的 Callback 的 handleMessage 方法處理谚鄙,最后才是讓 Handler 自身的 handleMessage 方法處理Message。

4.Callback

Callback 是 Handle r中的內(nèi)部接口刁绒,需要實(shí)現(xiàn)其內(nèi)部的 handleMessage 方法闷营,Callback 代碼如下:

    public interface Callback {
        public boolean handleMessage(Message msg);
    }

Handler.Callback 是用來處理 Message 的一種手段,如果沒有傳遞該參數(shù),那么就應(yīng)該重寫 Handler 的 handleMessage 方法傻盟,也就是說為了使得 Handler 能夠處理 Message 速蕊,有兩種辦法:

1.向 Hanlder 的構(gòu)造函數(shù)傳入一個(gè) Handler.Callback 對象,并實(shí)現(xiàn) Handler.Callback 的 handleMessage 方法
2.無需向 Hanlder 的構(gòu)造函數(shù)傳入 Handler.Callback 對象规哲,但是需要重寫 Handler 本身的 handleMessage 方法

也就是說無論哪種方式,我們都得通過某種方式實(shí)現(xiàn) handleMessage 方法诽表,這點(diǎn)與 Java 中對 Thread 的設(shè)計(jì)有異曲同工之處唉锌。
在Java中,如果我們想使用多線程竿奏,有兩種辦法:

1. 向 Thread 的構(gòu)造函數(shù)傳入一個(gè) Runnable 對象袄简,并實(shí)現(xiàn) Runnable 的 run 方法
2. 無需向 Thread 的構(gòu)造函數(shù)傳入 Runnable 對象,但是要重寫 Thread 本身的 run 方法

所以只要用過多線程 Thread议双,應(yīng)該就對 Hanlder 這種需要實(shí)現(xiàn) handleMessage 的兩種方式了然于心了痘番。
在之前分析 Handler(用法篇)的時(shí)候我們講到過兩種重要的方法:sendMessage() 和 post(),我們從源碼角度進(jìn)行進(jìn)一步分析平痰!

四 sendMessage


我們看下 sendMessage() 源碼處理流程:

public final boolean sendMessage(Message msg) {
    return sendMessageDelayed(msg, 0);
}

1.sendMessageDelayed

    public final boolean sendMessageDelayed(Message msg, long delayMillis)
    {
        if (delayMillis < 0) {
            delayMillis = 0;
        }
        return sendMessageAtTime(msg, SystemClock.uptimeMillis() + delayMillis);     // 最終調(diào) sendMessageAtTime()
    }

通過以上代碼可以看書:sendMessage() 調(diào)用了 sendMessageDelayed() ,sendMessageDelayed() 又調(diào)用了 sendMessageAtTime()伍纫。

Handler 中還有 sendEmptyMessage() 方法:

    public final boolean sendEmptyMessage(int what)
    {
        return sendEmptyMessageDelayed(what, 0);
    }
    public final boolean sendEmptyMessageDelayed(int what, long delayMillis) {
        Message msg = Message.obtain();
        msg.what = what;
        return sendMessageDelayed(msg, delayMillis);
    }
    public final boolean sendMessageDelayed(Message msg, long delayMillis)
    {
        if (delayMillis < 0) {
            delayMillis = 0;
        }
        return sendMessageAtTime(msg, SystemClock.uptimeMillis() + delayMillis);     // 最終還是要調(diào) sendMessageAtTime()
    }

由此可見所有的 sendMessage 方法和 sendEmptyMessage 最終都調(diào)用了 sendMessageAtTime 方法宗雇。

2.post

我們看下 post() 源碼處理流程:

    public final boolean post(Runnable r)
    {
       return  sendMessageDelayed(getPostMessage(r), 0);
    }

可以看到內(nèi)部調(diào)用了 getPostMessage 方法,該方法傳入一個(gè) Runnable 對象莹规,得到一個(gè) Message 對象赔蒲。

3.getPostMessage

getPostMessage() 的源碼如下:

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

通過上面的代碼我們可以看到在 getPostMessage 方法中,我們創(chuàng)建了一個(gè) Message 對象良漱,并將傳入的 Runnable 對象賦值給 Message 的 callback 成員字段舞虱,然后返回該 Message ,然后在 post 方法中該攜帶有 Runnable 信息的 Message 傳入到 sendMessageDelayed 方法中母市。由此我們可以看到所有的 post 方法內(nèi)部都需要借助 sendMessage 方法來實(shí)現(xiàn)矾兜,所以 post() 與 sendMessage() 并不是對立關(guān)系,而是 post() 依賴 sendMessage() 患久,所以 post() 方法可以通過 sendMessage() 方法向消息隊(duì)列中傳入消息椅寺,只不過通過 post() 方法向消息隊(duì)列中傳入的消息都攜帶有 Runnable 對象(Message.callback)。

五 sendMessageAtTime


通過分別分析 sendEmptyMessage()蒋失、post() 方法與 sendMessage() 方法之間的關(guān)系返帕,我們可以看到在 Handler 中所有可以直接或間接向消息隊(duì)列發(fā)送 Message 的方法最終都調(diào)用了 sendMessageAtTime 方法,該方法的源碼如下:

   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);
   }

1.enqueueMessage

我們發(fā)現(xiàn) sendMessageAtTime() 方法內(nèi)部調(diào)用了 enqueueMessage() 函數(shù):

   private boolean enqueueMessage(MessageQueue queue, Message msg, long uptimeMillis) {
       msg.target = this;
       if (mAsynchronous) {
           msg.setAsynchronous(true);
       }
       return queue.enqueueMessage(msg, uptimeMillis);
   }
我們需要重點(diǎn)注意兩行代碼:
msg.target = this; // 將 Message 的 target 綁定為當(dāng)前的 Handler
// 變量 queue 表示的是 Handler 所綁定的消息隊(duì)列 MessageQueue 篙挽,通過調(diào)用 queue.enqueueMessage(msg, uptimeMillis) 將 Message 放入到消息隊(duì)列中荆萤。
queue.enqueueMessage(msg, uptimeMillis);

還記得我們之前在分析 Looper 的時(shí)候,最終提到的 dispatchMessage() 嗎铣卡?我們回憶一下:

           try {
               msg.target.dispatchMessage(msg);
               end = (slowDispatchThresholdMs == 0) ? 0 : SystemClock.uptimeMillis();
           } finally {
               if (traceTag != 0) {
                   Trace.traceEnd(traceTag);
               }
           }

六 MessageQueue


每個(gè)線程內(nèi)部都維護(hù)了一個(gè)消息隊(duì)列 —— MessageQueue链韭。消息隊(duì)列 MessageQueue邑闲,顧名思義,就是存放消息的隊(duì)列梧油。那隊(duì)列中存儲(chǔ)的消息是什么呢苫耸?

假設(shè)我們在UI界面上單擊了某個(gè)按鈕,而此時(shí)程序又恰好收到了某個(gè)廣播事件儡陨,那我們?nèi)绾翁幚磉@兩件事呢褪子?因?yàn)橐粋€(gè)線程在某一時(shí)刻只能處理一件事情,不能同時(shí)處理多件事情骗村,所以我們不能同時(shí)處理按鈕的單擊事件和廣播事件嫌褪,我們只能挨個(gè)對其進(jìn)行處理,只要挨個(gè)處理就要有處理的先后順序胚股。

為此Android把UI界面上單擊按鈕的事件封裝成了一個(gè) Message 笼痛,將其放入到 MessageQueue 里面去,即將單擊按鈕事件的 Message 入棧到消息隊(duì)列中琅拌,然后再將廣播事件的封裝成以 Message 缨伊,也將其入棧到消息隊(duì)列中。

也就是說一個(gè) Message 對象表示的是線程需要處理的一件事情进宝,消息隊(duì)列就是一堆需要處理的 Message 的池刻坊。線程 Thread 會(huì)依次取出消息隊(duì)列中的消息,依次對其進(jìn)行處理党晋。

MessageQueue 中有兩個(gè)比較重要的方法谭胚,一個(gè)是 enqueueMessage 方法,一個(gè)是 next 方法未玻。enqueueMessage 方法用于將一個(gè) Messag e放入到消息隊(duì)列 MessageQueue 中灾而,next 方法是從消息隊(duì)列 MessageQueue 中阻塞式地取出一個(gè) Message。在 Android 中扳剿,消息隊(duì)列負(fù)責(zé)管理著頂級(jí)程序?qū)ο螅ˋctivity旁趟、BroadcastReceiver等)以及由其創(chuàng)建的所有窗口。

七 創(chuàng)建MessageQueue


   MessageQueue(boolean quitAllowed) {
       mQuitAllowed = quitAllowed;
       // 通過 native 方法初始化消息隊(duì)列舞终,其中 mPtr 是供 native 代碼使用
       mPtr = nativeInit();
   }

next()

   Message next() {
       final long ptr = mPtr;
       if (ptr == 0) {     // 當(dāng)消息循環(huán)已經(jīng)退出轻庆,則直接返回
           return null;
       }

       // 循環(huán)迭代的首次為 -1
       int pendingIdleHandlerCount = -1; // -1 only during first iteration
       int nextPollTimeoutMillis = 0;
       for (;;) {
           if (nextPollTimeoutMillis != 0) {
               Binder.flushPendingCommands();
           }

           // 阻塞操作,當(dāng)?shù)却?nextPollTimeoutMillis 時(shí)長敛劝,或者消息隊(duì)列被喚醒余爆,都會(huì)返回
           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) {
                   // 當(dāng)消息 Handler 為空時(shí),查詢 MessageQueue 中的下一條異步消息 msg夸盟,則退出循環(huán)
                   do {
                       prevMsg = msg;
                       msg = msg.next;
                   } while (msg != null && !msg.isAsynchronous());
               }
               if (msg != null) {
                   if (now < msg.when) {
                       // 當(dāng)異步消息觸發(fā)時(shí)間大于當(dāng)前時(shí)間蛾方,則設(shè)置下一次輪詢的超時(shí)時(shí)長
                       nextPollTimeoutMillis = (int) Math.min(msg.when - now, Integer.MAX_VALUE);
                   } else {
                       // 獲取一條消息,并返回
                       mBlocked = false;
                       if (prevMsg != null) {
                           prevMsg.next = msg.next;
                       } else {
                           mMessages = msg.next;
                       }
                       msg.next = null;
                       // 設(shè)置消息的使用狀態(tài),即 flags |= FLAG_IN_USE
                       msg.markInUse();
                       // 成功地獲取 MessageQueue 中的下一條即將要執(zhí)行的消息
                       return msg;
                   }
               } else {
                   // 沒有消息
                   nextPollTimeoutMillis = -1;
               }

               // 消息正在退出桩砰,返回null
               if (mQuitting) {
                   dispose();
                   return null;
               }

               // 當(dāng)消息隊(duì)列為空拓春,或者是消息隊(duì)列的第一個(gè)消息時(shí)
               if (pendingIdleHandlerCount < 0
                       && (mMessages == null || now < mMessages.when)) {
                   pendingIdleHandlerCount = mIdleHandlers.size();
               }
               if (pendingIdleHandlerCount <= 0) {
               // 沒有 idle handlers 需要運(yùn)行,則循環(huán)并等待
                   mBlocked = true;
                   continue;
               }

               if (mPendingIdleHandlers == null) {
                   mPendingIdleHandlers = new IdleHandler[Math.max(pendingIdleHandlerCount, 4)];
               }
               mPendingIdleHandlers = mIdleHandlers.toArray(mPendingIdleHandlers);
           }

           // 只有第一次循環(huán)時(shí)亚隅,會(huì)運(yùn)行 idle handlers硼莽,執(zhí)行完成后,重置 pendingIdleHandlerCount 為 0
           for (int i = 0; i < pendingIdleHandlerCount; i++) {
               final IdleHandler idler = mPendingIdleHandlers[i];
               mPendingIdleHandlers[i] = null; // 去掉 handler 的引用

               boolean keep = false;
               try {
                   keep = idler.queueIdle();   // idle 時(shí)執(zhí)行的方法
               } catch (Throwable t) {
                   Log.wtf(TAG, "IdleHandler threw exception", t);
               }

               if (!keep) {
                   synchronized (this) {
                       mIdleHandlers.remove(idler);
                   }
               }
           }

           // 重置 idle handler 個(gè)數(shù)為 0煮纵,以保證不會(huì)再次重復(fù)運(yùn)行
           pendingIdleHandlerCount = 0;

           // 當(dāng)調(diào)用一個(gè)空閑 handler 時(shí)懂鸵,一個(gè)新 message 能夠被分發(fā),因此無需等待可以直接查詢 pending message
           nextPollTimeoutMillis = 0;
       }
   }

nativePollOnce 是阻塞操作行疏,其中 nextPollTimeoutMillis 代表下一個(gè)消息到來前匆光,還需要等待的時(shí)長;當(dāng) nextPollTimeoutMillis = -1 時(shí)酿联,表示消息隊(duì)列中無消息终息,會(huì)一直等待下去。

當(dāng)處于空閑時(shí)贞让,往往會(huì)執(zhí)行 IdleHandler 中的方法周崭。當(dāng) nativePollOnce() 返回后,next() 從 mMessages 中提取一個(gè)消息震桶。

八 enqueueMessage()

    boolean enqueueMessage(Message msg, long when) {
        // 每一個(gè)普通 Message 必須有一個(gè) target
        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) {
            // 正在退出時(shí)休傍,回收 msg,加入到消息池
            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;
            if (p == null || when == 0 || when < p.when) {
                // p 為 null (代表MessageQueue沒有消息) 或者 msg 的觸發(fā)時(shí)間是隊(duì)列中最早的蹲姐,則進(jìn)入該該分支
                msg.next = p;
                mMessages = msg;
                needWake = mBlocked;   // 當(dāng)阻塞時(shí)需要喚醒
            } else {
                // 將消息按時(shí)間順序插入到 MessageQueue。一般地人柿,不需要喚醒事件隊(duì)列柴墩,除非
                // 消息隊(duì)頭存在 barrier,并且同時(shí) Message 是隊(duì)列中最早的異步消息
                needWake = mBlocked && p.target == null && msg.isAsynchronous();
                Message prev;
                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;
            }

            // 消息沒有退出凫岖,我們認(rèn)為此時(shí) mPtr != 0
            if (needWake) {
                nativeWake(mPtr);
            }
        }
        return true;
    }

MessageQueue 是按照 Message 觸發(fā)時(shí)間的先后順序排列的江咳,隊(duì)頭的消息是將要最早觸發(fā)的消息。當(dāng)有消息需要加入消息隊(duì)列時(shí)哥放,會(huì)從隊(duì)列頭開始遍歷歼指,直到找到消息應(yīng)該插入的合適位置,以保證所有消息的時(shí)間順序甥雕。

九 removeMessages()


   void removeMessages(Handler h, int what, Object object) {
       if (h == null) {
           return;
       }

       synchronized (this) {
           Message p = mMessages;

           // 從消息隊(duì)列的頭部開始踩身,移除所有符合條件的消息
           while (p != null && p.target == h && p.what == what
                  && (object == null || p.obj == object)) {
               Message n = p.next;
               mMessages = n;
               p.recycleUnchecked();
               p = n;
           }

           // 移除剩余的符合要求的消息
           while (p != null) {
               Message n = p.next;
               if (n != null) {
                   if (n.target == h && n.what == what
                       && (object == null || n.obj == object)) {
                       Message nn = n.next;
                       n.recycleUnchecked();
                       p.next = nn;
                       continue;
                   }
               }
               p = n;
           }
       }
   }

這個(gè)移除消息的方法,采用了兩個(gè) while 循環(huán)社露,第一個(gè)循環(huán)是從隊(duì)頭開始挟阻,移除符合條件的消息,第二個(gè)循環(huán)是從頭部移除完連續(xù)的滿足條件的消息之后,再從隊(duì)列后面繼續(xù)查詢是否有滿足條件的消息需要被移除附鸽。

總結(jié)


最后用一張圖脱拼,來表示整個(gè)消息機(jī)制:


63b637532d65d2a632369cbe8315b5bb_1460000016798930.jpg

圖解:

    ? Handler通過sendMessage()發(fā)送Message到MessageQueue隊(duì)列;
    ? Looper通過loop()坷备,不斷提取出達(dá)到觸發(fā)條件的Message熄浓,并將Message交給target來處理;
    ? 經(jīng)過dispatchMessage()后省撑,交回給Handler的handleMessage()來進(jìn)行相應(yīng)地處理赌蔑。
    ? 將Message加入MessageQueue時(shí),處往管道寫入字符丁侄,可以會(huì)喚醒loop線程惯雳;如果MessageQueue中沒有Message,并處于Idle狀態(tài)鸿摇,則會(huì)執(zhí)行IdelHandler接口中的方法石景,往往用于做一些清理性地工作。

想學(xué)習(xí)更多Android知識(shí)拙吉,或者獲取相關(guān)資料請加入Android技術(shù)開發(fā)交流2群:935654177潮孽。本群可免費(fèi)獲取Gradle,RxJava筷黔,小程序往史,Hybrid,移動(dòng)架構(gòu)佛舱,NDK椎例,React Native,性能優(yōu)化等技術(shù)教程请祖!

面向Android中的一切實(shí)體.png
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末订歪,一起剝皮案震驚了整個(gè)濱河市,隨后出現(xiàn)的幾起案子肆捕,更是在濱河造成了極大的恐慌刷晋,老刑警劉巖,帶你破解...
    沈念sama閱讀 207,248評(píng)論 6 481
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件慎陵,死亡現(xiàn)場離奇詭異眼虱,居然都是意外死亡,警方通過查閱死者的電腦和手機(jī)席纽,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 88,681評(píng)論 2 381
  • 文/潘曉璐 我一進(jìn)店門捏悬,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人胆筒,你說我怎么就攤上這事邮破≌┩悖” “怎么了?”我有些...
    開封第一講書人閱讀 153,443評(píng)論 0 344
  • 文/不壞的土叔 我叫張陵抒和,是天一觀的道長矫渔。 經(jīng)常有香客問我,道長摧莽,這世上最難降的妖魔是什么庙洼? 我笑而不...
    開封第一講書人閱讀 55,475評(píng)論 1 279
  • 正文 為了忘掉前任,我火速辦了婚禮镊辕,結(jié)果婚禮上油够,老公的妹妹穿的比我還像新娘。我一直安慰自己征懈,他們只是感情好石咬,可當(dāng)我...
    茶點(diǎn)故事閱讀 64,458評(píng)論 5 374
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著卖哎,像睡著了一般鬼悠。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上亏娜,一...
    開封第一講書人閱讀 49,185評(píng)論 1 284
  • 那天焕窝,我揣著相機(jī)與錄音,去河邊找鬼维贺。 笑死它掂,一個(gè)胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的溯泣。 我是一名探鬼主播虐秋,決...
    沈念sama閱讀 38,451評(píng)論 3 401
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼垃沦!你這毒婦竟也來了熟妓?” 一聲冷哼從身側(cè)響起,我...
    開封第一講書人閱讀 37,112評(píng)論 0 261
  • 序言:老撾萬榮一對情侶失蹤栏尚,失蹤者是張志新(化名)和其女友劉穎,沒想到半個(gè)月后只恨,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體译仗,經(jīng)...
    沈念sama閱讀 43,609評(píng)論 1 300
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 36,083評(píng)論 2 325
  • 正文 我和宋清朗相戀三年官觅,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了纵菌。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點(diǎn)故事閱讀 38,163評(píng)論 1 334
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡休涤,死狀恐怖咱圆,靈堂內(nèi)的尸體忽然破棺而出笛辟,到底是詐尸還是另有隱情,我是刑警寧澤序苏,帶...
    沈念sama閱讀 33,803評(píng)論 4 323
  • 正文 年R本政府宣布手幢,位于F島的核電站,受9級(jí)特大地震影響忱详,放射性物質(zhì)發(fā)生泄漏围来。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 39,357評(píng)論 3 307
  • 文/蒙蒙 一匈睁、第九天 我趴在偏房一處隱蔽的房頂上張望监透。 院中可真熱鬧,春花似錦航唆、人聲如沸胀蛮。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,357評(píng)論 0 19
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽粪狼。三九已至,卻和暖如春超营,著一層夾襖步出監(jiān)牢的瞬間鸳玩,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 31,590評(píng)論 1 261
  • 我被黑心中介騙來泰國打工演闭, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留不跟,地道東北人。 一個(gè)月前我還...
    沈念sama閱讀 45,636評(píng)論 2 355
  • 正文 我出身青樓米碰,卻偏偏與公主長得像窝革,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個(gè)殘疾皇子吕座,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 42,925評(píng)論 2 344

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

  • 如是我聞: 大體而言吴趴,當(dāng)感知來自嗔恨時(shí)漆诽,你是在體驗(yàn)地獄道;當(dāng)感知來自執(zhí)著锣枝、執(zhí)取或貪吝厢拭,你在體驗(yàn)餓鬼道;當(dāng)你的感知通...
    睡一夜就餿星人閱讀 1,987評(píng)論 0 4
  • 今天總算晴天了撇叁,但是零下八度供鸠,這天氣說冷就冷,也太快了吧陨闹! 今天兒子數(shù)學(xué)考試楞捂,我給他加油打氣薄坏,可是...
    擎宇寶貝閱讀 145評(píng)論 0 0
  • 陽光不冷不熱,微風(fēng)不急不躁寨闹。 第一次坐地鐵胶坠,感受很特別!從外觀看和高鐵有類似的感覺鼻忠,踏入車門別有一番風(fēng)...
    阿沫a閱讀 184評(píng)論 0 4
  • 親愛的老弟: 儂好涵但!嗯,你應(yīng)該在期末備考中帖蔓,很好矮瘟! 早就想提筆寫這封信,卻因?yàn)楫厴I(yè)答辯塑娇、就業(yè)入職等瑣事一拖再拖澈侠,如...
    杉水Daisy閱讀 1,516評(píng)論 0 50