Handler運(yùn)行機(jī)制

Android 消息機(jī)制就是handler的運(yùn)行機(jī)制以及MessageQueue和Looper的工作過(guò)程耸三。

那么Android的消息機(jī)制為什么要用Handler呢?
在Android中泄鹏,UI線程是非線程安全的臊旭,所以規(guī)定只能在主線程中訪問(wèn)UI線程。而且主線程中不能進(jìn)行耗時(shí)的操作量没,所以把耗時(shí)操作放在異步線程中執(zhí)行艘款。那如何讓子線程中的獲取的數(shù)據(jù)在主線程中進(jìn)行使用呢持际,這個(gè)時(shí)候就用到了Handler。

Handler的運(yùn)行機(jī)制 少不了Handler Message MessageQueue Looper幾個(gè)類(lèi)的支撐哗咆,下面分別對(duì)每個(gè)類(lèi)進(jìn)行介紹蜘欲。

Handler

A Handler allows you to send and process {@link Message} and Runnable objects associated with a thread's {@link MessageQueue}. Each Handler instance is associated with a single thread and that thread's message queue. When you create a new Handler, it is bound to the thread /message queue of the thread that is creating it -- from that point on, it will deliver messages and runnables to that message queue and execute them as they come out of the message queue.
大致意思:handler 通過(guò)關(guān)聯(lián)一個(gè)消息隊(duì)列來(lái)發(fā)送和處理消息,發(fā)送或處理Runnable對(duì)象

Handler的作用

There are two main uses for a Handler: (1) to schedule messages and runnables to be executed as some point in the future; and (2) to enqueue an action to be performed on a different thread than your own.
大致意思:handler 通過(guò)關(guān)聯(lián)一個(gè)消息隊(duì)列來(lái)發(fā)送和處理消息晌柬,發(fā)送或處理Runnable對(duì)象

  • 在未來(lái)某個(gè)時(shí)間點(diǎn)執(zhí)行分發(fā)的message/runnable
  • 在其他線程執(zhí)行action

介紹完handler 來(lái)看看handler的 constructor的實(shí)現(xiàn)

    public Handler(Callback callback, boolean async) {
        if (FIND_POTENTIAL_LEAKS) {
            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());
            }
        }

        mLooper = Looper.myLooper();
        if (mLooper == null) {
            throw new RuntimeException(
                "Can't create handler inside thread " + Thread.currentThread()
                        + " that has not called Looper.prepare()");
        }
        mQueue = mLooper.mQueue;
        mCallback = callback;
        mAsynchronous = async;
    }

首先檢測(cè)用戶創(chuàng)建的handler類(lèi)是不是static的姥份,如果不是則會(huì)提示The following Handler class should be static or leaks might occur郭脂,意思就說(shuō)會(huì)有導(dǎo)致內(nèi)存泄露的可能。
在子線程中如果沒(méi)有創(chuàng)建looper澈歉,則會(huì)拋出異痴辜Γ“has not called Looper.prepare()”,因?yàn)樵谥骶€程中闷祥,系統(tǒng)默認(rèn)就會(huì)創(chuàng)建looper,所以我們平時(shí)使用handler時(shí)傲诵,沒(méi)有創(chuàng)建looper是不報(bào)錯(cuò)的凯砍,但是在子線程中會(huì)拋出異常的,接著就是將handler和MessageQueue進(jìn)行關(guān)聯(lián)拴竹。

Handler的主要工作包含消息的發(fā)送和接收悟衩,先分析發(fā)送:

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

  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 sendEmptyMessageAtTime(int what, long uptimeMillis) {
        Message msg = Message.obtain();
        msg.what = what;
        return sendMessageAtTime(msg, uptimeMillis);
    }
  //上面的方法最終都會(huì)走到這里,這個(gè)方法主要功能還是實(shí)現(xiàn)了enqueueMessage
  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);
    }

接著分析enqueueMessage:

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

可以看到栓拜,Handler發(fā)送消息的過(guò)程就是向MessageQueue中插入一條消息座泳,下面會(huì)講到MessageQueue通過(guò)next方法獲取出消息,然后返回給looper幕与,在looper的loop方法中執(zhí)行了msg.target.dispatchMessage(msg); msg.target 就是指的handler挑势。那么接著分析dispatchMessage:

    /**
     * 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的callback就是一個(gè)Runnable對(duì)象,日常使用handler時(shí)會(huì)重寫(xiě)handlerMessage方法去處理消息啦鸣,而callback的意義就是當(dāng)我們不想實(shí)現(xiàn)handlerMessage方法時(shí)潮饱,就可以用callback代替。

Looper

used to run a message loop for a thread 簡(jiǎn)單的說(shuō)就是 為一個(gè)線程進(jìn)行消息輪詢诫给,典型的使用案例如下代碼所示:

  * <pre>
  *  class LooperThread extends Thread {
  *      public Handler mHandler;
  *
  *      public void run() {
  *          Looper.prepare();
  *
  *          mHandler = new Handler() {
  *              public void handleMessage(Message msg) {
  *                  // process incoming messages here
  *              }
  *          };
  *
  *          Looper.loop();
  *      }
  *  }</pre>

在創(chuàng)建handler之前執(zhí)行Looper.prepare()香拉, 創(chuàng)建handler之后執(zhí)行Looper.loop()即可。

    /** Initialize the current thread as a looper.
      * This gives you a chance to create handlers that then reference
      * this looper, before actually starting the loop. Be sure to call
      * {@link #loop()} after calling this method, and end it by calling
      * {@link #quit()}.
      */
    public static void prepare() {
        prepare(true);
    }

作用時(shí)在確定執(zhí)行輪詢(loop)之前中狂,創(chuàng)建handler并關(guān)聯(lián)looper凫碌,執(zhí)行prepare后確定執(zhí)行l(wèi)oop,結(jié)束用quit胃榕。接著跟蹤下:

prepare(boolean)方法

這里面的入?yún)oolean表示Looper是否允許退出盛险,true就表示允許退出,對(duì)于false則表示Looper不允許退出勋又。

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

    /**
     * Initialize the current thread as a looper, marking it as an
     * application's main looper. The main looper for your application
     * is created by the Android environment, so you should never need
     * to call this function yourself.  See also: {@link #prepare()}
     */
 public static void prepareMainLooper() {
        prepare(false);
        synchronized (Looper.class) {
            if (sMainLooper != null) {
                throw new IllegalStateException("The main Looper has already been prepared.");
            }
            sMainLooper = myLooper();
        }
    }

上面是子線程創(chuàng)建handler必須要調(diào)用的枉层,后者是應(yīng)用創(chuàng)建主線程時(shí)調(diào)用,使用者可不理會(huì)赐写。關(guān)于sThreadLocal 后面文章會(huì)繼續(xù)講解鸟蜡。接著看下Looper的構(gòu)造方法:

Looper(boolean)構(gòu)造函數(shù)

private Looper(boolean quitAllowed) {
        // 創(chuàng)建MessageQueue對(duì)象
        mQueue = new MessageQueue(quitAllowed);
        // 記錄當(dāng)前線程
        mThread = Thread.currentThread();
    }

通過(guò)上面代碼,我們發(fā)現(xiàn)就是創(chuàng)建了一個(gè)MessageQueue挺邀,并且把當(dāng)前線程賦值給本地變量的mThread揉忘。這里就實(shí)現(xiàn)了Looper和MessageQueue跳座,Thread的關(guān)聯(lián),也知道Looper類(lèi)不是通過(guò)構(gòu)造函數(shù)直接來(lái)創(chuàng)建looper泣矛,而是通過(guò)靜態(tài)方法調(diào)用間接創(chuàng)建Looper類(lèi)疲眷。前面我們講了Looper的作用就是為線程輪詢消息,接著看真正的實(shí)現(xiàn):

    /**
     * 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;
        boolean slowDeliveryDetected = false;

        for (;;) {
            Message msg = queue.next(); // might block
            if (msg == null) {
                // No message indicates that the message queue is quitting.
                return;
            }
...
            final long traceTag = me.mTraceTag;
            long slowDispatchThresholdMs = me.mSlowDispatchThresholdMs;
            long slowDeliveryThresholdMs = me.mSlowDeliveryThresholdMs;
            if (thresholdOverride > 0) {
                slowDispatchThresholdMs = thresholdOverride;
                slowDeliveryThresholdMs = thresholdOverride;
            }
            final boolean logSlowDelivery = (slowDeliveryThresholdMs > 0) && (msg.when > 0);
            final boolean logSlowDispatch = (slowDispatchThresholdMs > 0);

            final boolean needStartTime = logSlowDelivery || logSlowDispatch;
            final boolean needEndTime = logSlowDispatch;

            if (traceTag != 0 && Trace.isTagEnabled(traceTag)) {
                Trace.traceBegin(traceTag, msg.target.getTraceName(msg));
            }

            final long dispatchStart = needStartTime ? SystemClock.uptimeMillis() : 0;
            final long dispatchEnd;
            try {
                msg.target.dispatchMessage(msg);
                dispatchEnd = needEndTime ? SystemClock.uptimeMillis() : 0;
            } finally {
                if (traceTag != 0) {
                    Trace.traceEnd(traceTag);
                }
            }
    ...
            msg.recycleUnchecked();
        }
    }

通過(guò)queue.next() 獲取消息您朽,當(dāng)消息為null的時(shí)候狂丝,MessageQueue就會(huì)調(diào)用quit或者quitSafety方法并被標(biāo)記為退出狀態(tài),當(dāng)消息隊(duì)列被標(biāo)記為退出狀態(tài)時(shí)哗总,next方法就會(huì)返回null几颜。換句話說(shuō),loop方法會(huì)調(diào)用MessageQueue的next方法來(lái)獲取新消息讯屈,next方法是一個(gè)阻塞操作蛋哭,當(dāng)沒(méi)有消息時(shí),next方法會(huì)一致阻塞涮母,loop方法也會(huì)被阻塞谆趾,當(dāng)有新消息到達(dá)時(shí),Looper就會(huì)繼續(xù)處理消息了叛本。
msg.target.dispatchMessage(msg)分發(fā)消息沪蓬,msg.target 就是指當(dāng)前線程綁定的handler,msg.recycleUnchecked() 將消息放入消息池来候。

Message

Defines a message containing a description and arbitrary data object that can be sent to a {@link Handler}. This object contains two extra int fields and an extra object field that allow you to not do allocations in many cases.
一個(gè)實(shí)現(xiàn)了序列化的對(duì)象怜跑。接著看一下obtion方法:

    /**
     * Same as {@link #obtain()}, but sets the value for the <em>target</em> member on the Message returned.
     * @param h  Handler to assign to the returned Message object's <em>target</em> member.
     * @return A Message object from the global pool.
     */
    public static Message obtain(Handler h) {
        Message m = obtain();
        m.target = h;

        return m;
    }

可以看到message.target = handler ,就可以理解了上面Looper.loop()方法分發(fā)消息的target 就是handler,可以知道先是handler發(fā)送消息吠勘,然后handler分發(fā)消息性芬,最后自己實(shí)現(xiàn)handleMessage方法處理消息,其他obtain方法大同小異剧防。

MessageQueue

MessageQueue主要包含兩個(gè)功能植锉,enqueueMessage和next。
先看一下enqueueMessage:

    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;
            if (p == null || when == 0 || when < p.when) {
                // New head, wake up the event queue if blocked.
                msg.next = p;
                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.
                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;
            }

            // We can assume mPtr != 0 because mQuitting is false.
            if (needWake) {
                nativeWake(mPtr);
            }
        }
        return true;
    }

從實(shí)現(xiàn)來(lái)看主要就是單鏈表的插入操作峭拘,接著再看next方法:

    Message next() {
        // Return here if the message loop has already quit and been disposed.
        // This can happen if the application tries to restart a looper after quit
        // which is not supported.
        final long ptr = mPtr;
        if (ptr == 0) {
            return null;
        }

        int pendingIdleHandlerCount = -1; // -1 only during first iteration
        int nextPollTimeoutMillis = 0;
        for (;;) {
            if (nextPollTimeoutMillis != 0) {
                Binder.flushPendingCommands();
            }

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

                // Process the quit message now that all pending messages have been handled.
                if (mQuitting) {
                    dispose();
                    return null;
                }

                // If first time idle, then get the number of idlers to run.
                // Idle handles only run if the queue is empty or if the first message
                // in the queue (possibly a barrier) is due to be handled in the future.
                if (pendingIdleHandlerCount < 0
                        && (mMessages == null || now < mMessages.when)) {
                    pendingIdleHandlerCount = mIdleHandlers.size();
                }
                if (pendingIdleHandlerCount <= 0) {
                    // No idle handlers to run.  Loop and wait some more.
                    mBlocked = true;
                    continue;
                }

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

            // Run the idle handlers.
            // We only ever reach this code block during the first iteration.
            for (int i = 0; i < pendingIdleHandlerCount; i++) {
                final IdleHandler idler = mPendingIdleHandlers[i];
                mPendingIdleHandlers[i] = null; // release the reference to the handler

                boolean keep = false;
                try {
                    keep = idler.queueIdle();
                } catch (Throwable t) {
                    Log.wtf(TAG, "IdleHandler threw exception", t);
                }

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

            // Reset the idle handler count to 0 so we do not run them again.
            pendingIdleHandlerCount = 0;

            // While calling an idle handler, a new message could have been delivered
            // so go back and look again for a pending message without waiting.
            nextPollTimeoutMillis = 0;
        }
    }

next方法中也有一個(gè)for循環(huán)俊庇,如果有新消息,next方法會(huì)retrun這條消息并且從鏈表中移除鸡挠,否則的話會(huì)一直阻塞辉饱,直到有新消息。

總結(jié):

  1. Looper類(lèi)用來(lái)為一個(gè)線程開(kāi)啟一個(gè)消息循環(huán)拣展。默認(rèn)情況下android中新創(chuàng)建的線程是未開(kāi)啟消息循環(huán)的彭沼。(主線程除外,主線程系統(tǒng)會(huì)自動(dòng)為其創(chuàng)建Looper對(duì)象备埃,開(kāi)啟消息循環(huán)姓惑。)
    Looper對(duì)象通過(guò)MessageQueue來(lái)存放消息和事件褐奴。一個(gè)線程只能有一個(gè)Looper,對(duì)應(yīng)一個(gè)MessageQueue于毙。(如果對(duì)一個(gè)已經(jīng)quit的Looper重新start會(huì)出現(xiàn)異常)

  2. 通常是通過(guò)Handler與Looper進(jìn)行交互的敦冬,Handler可看做是Looper的一個(gè)接口,用來(lái)向指定的Looper發(fā)送消息及定義處理方法唯沮。
    默認(rèn)情況下Handler會(huì)與其被定義時(shí)所在線程的Looper綁定脖旱,比如,Handler在主線程中定義介蛉,那么它是與主線程的Looper綁定萌庆。
    mainHandler = new Handler() 等價(jià)于new Handler(Looper.myLooper()).
    Looper.myLooper():獲取當(dāng)前進(jìn)程的looper對(duì)象,類(lèi)似的 Looper.getMainLooper() 用于獲取主線程的Looper對(duì)象甘耿。

  3. 在非主線程中直接new Handler() 會(huì)報(bào)如下的錯(cuò)誤:
    E/AndroidRuntime( 6173): Uncaught handler: thread Thread-8 exiting due to uncaught exception
    E/AndroidRuntime( 6173): java.lang.RuntimeException: Can't create handler inside thread that has not called Looper.prepare()
    原因是非主線程中默認(rèn)沒(méi)有創(chuàng)建Looper對(duì)象踊兜,需要先調(diào)用Looper.prepare()啟用Looper竿滨。

  4. Looper.loop()讓Looper開(kāi)始工作佳恬,從消息隊(duì)列里取消息,處理消息于游。注意:寫(xiě)在Looper.loop()之后的代碼不會(huì)被執(zhí)行毁葱,這個(gè)函數(shù)內(nèi)部應(yīng)該是一個(gè)循環(huán),當(dāng)調(diào)用mHandler.getLooper().quit()后贰剥,loop才會(huì)中止倾剿,其后的代碼才能得以運(yùn)行。

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末蚌成,一起剝皮案震驚了整個(gè)濱河市前痘,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌担忧,老刑警劉巖芹缔,帶你破解...
    沈念sama閱讀 210,835評(píng)論 6 490
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場(chǎng)離奇詭異瓶盛,居然都是意外死亡最欠,警方通過(guò)查閱死者的電腦和手機(jī),發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 89,900評(píng)論 2 383
  • 文/潘曉璐 我一進(jìn)店門(mén)惩猫,熙熙樓的掌柜王于貴愁眉苦臉地迎上來(lái)芝硬,“玉大人,你說(shuō)我怎么就攤上這事轧房“枰酰” “怎么了?”我有些...
    開(kāi)封第一講書(shū)人閱讀 156,481評(píng)論 0 345
  • 文/不壞的土叔 我叫張陵奶镶,是天一觀的道長(zhǎng)皮官。 經(jīng)常有香客問(wèn)我脯倒,道長(zhǎng),這世上最難降的妖魔是什么捺氢? 我笑而不...
    開(kāi)封第一講書(shū)人閱讀 56,303評(píng)論 1 282
  • 正文 為了忘掉前任藻丢,我火速辦了婚禮,結(jié)果婚禮上摄乒,老公的妹妹穿的比我還像新娘悠反。我一直安慰自己,他們只是感情好馍佑,可當(dāng)我...
    茶點(diǎn)故事閱讀 65,375評(píng)論 5 384
  • 文/花漫 我一把揭開(kāi)白布斋否。 她就那樣靜靜地躺著,像睡著了一般拭荤。 火紅的嫁衣襯著肌膚如雪茵臭。 梳的紋絲不亂的頭發(fā)上,一...
    開(kāi)封第一講書(shū)人閱讀 49,729評(píng)論 1 289
  • 那天舅世,我揣著相機(jī)與錄音旦委,去河邊找鬼。 笑死雏亚,一個(gè)胖子當(dāng)著我的面吹牛缨硝,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播罢低,決...
    沈念sama閱讀 38,877評(píng)論 3 404
  • 文/蒼蘭香墨 我猛地睜開(kāi)眼查辩,長(zhǎng)吁一口氣:“原來(lái)是場(chǎng)噩夢(mèng)啊……” “哼!你這毒婦竟也來(lái)了网持?” 一聲冷哼從身側(cè)響起宜岛,我...
    開(kāi)封第一講書(shū)人閱讀 37,633評(píng)論 0 266
  • 序言:老撾萬(wàn)榮一對(duì)情侶失蹤,失蹤者是張志新(化名)和其女友劉穎功舀,沒(méi)想到半個(gè)月后萍倡,有當(dāng)?shù)厝嗽跇?shù)林里發(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 44,088評(píng)論 1 303
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡日杈,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 36,443評(píng)論 2 326
  • 正文 我和宋清朗相戀三年遣铝,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片莉擒。...
    茶點(diǎn)故事閱讀 38,563評(píng)論 1 339
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡酿炸,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出涨冀,到底是詐尸還是另有隱情填硕,我是刑警寧澤,帶...
    沈念sama閱讀 34,251評(píng)論 4 328
  • 正文 年R本政府宣布,位于F島的核電站扁眯,受9級(jí)特大地震影響壮莹,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜姻檀,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 39,827評(píng)論 3 312
  • 文/蒙蒙 一命满、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧绣版,春花似錦胶台、人聲如沸。這莊子的主人今日做“春日...
    開(kāi)封第一講書(shū)人閱讀 30,712評(píng)論 0 21
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)。三九已至缩麸,卻和暖如春铸磅,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背杭朱。 一陣腳步聲響...
    開(kāi)封第一講書(shū)人閱讀 31,943評(píng)論 1 264
  • 我被黑心中介騙來(lái)泰國(guó)打工阅仔, 沒(méi)想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留,地道東北人痕檬。 一個(gè)月前我還...
    沈念sama閱讀 46,240評(píng)論 2 360
  • 正文 我出身青樓霎槐,卻偏偏與公主長(zhǎng)得像送浊,于是被迫代替她去往敵國(guó)和親梦谜。 傳聞我的和親對(duì)象是個(gè)殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 43,435評(píng)論 2 348

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