一起精讀源代碼系列(2) Android Handler源碼分析

做Android研發(fā)的同學(xué)不管是面試還是平時(shí)的工作中一定會(huì)遇到各種Handler相關(guān)的問題赶站,這一篇我們就通過源代碼一起探討一下Android的Handler機(jī)制。本文的分析基于Android8.0源碼。
1、一個(gè)小問題,有代碼如下悬而。一個(gè)函數(shù)延遲2秒執(zhí)行,另一個(gè)函數(shù)延遲3秒執(zhí)行锭汛,其中第一個(gè)函數(shù)執(zhí)行了3秒笨奠,請(qǐng)問第二個(gè)函數(shù)會(huì)在什么時(shí)候執(zhí)行袭蝗。

        Handler handler = new Handler() {
            @Override
            public void handleMessage(Message msg) {
                super.handleMessage(msg);
                switch (msg.what) {
                    case 1:
                        methodOne();
                        break;
                    case 2:
                        methodTwo();
                        break;
                    default:
                        break;
                }
            }
        };
        handler.sendEmptyMessageDelayed(1, 2000);
        handler.sendEmptyMessageDelayed(2, 3000);

    private void methodTwo() {
        System.out.println("methodTwo");
    }

    private void methodOne() {
        System.out.println("methodOne");
        try {
            Thread.sleep(3000);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        System.out.println("methodOneFinish");
    }

帶著這個(gè)問題,開始本文分析般婆。
先看構(gòu)造函數(shù)

    public Handler() {
        this(null, false);
    }
    public Handler(Looper looper) {
        this(looper, null, false);
    }
    public Handler(Looper looper, Callback callback) {
        this(looper, callback, false);
    }
    public Handler(boolean async) {
        this(null, async);
    }
    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 that has not called Looper.prepare()");
        }
        mQueue = mLooper.mQueue;
        mCallback = callback;
        mAsynchronous = async;
    }

    public Handler(Looper looper, Callback callback, boolean async) {
        mLooper = looper;
        mQueue = looper.mQueue;
        mCallback = callback;
        mAsynchronous = async;
    }

默認(rèn)構(gòu)造函數(shù)callback為null到腥,async為false,async如果為true蔚袍,則處理程序?qū)⒄{(diào)用Message#setAsynchronous乡范,looper就是Looper.myLooper()的返回值,是從ThreadLocal中g(shù)et到的一個(gè)值,也就是如下代碼

    public static @Nullable Looper myLooper() {
        return sThreadLocal.get();
    }

跟蹤到ThreadLocal的get函數(shù)

    public T get() {
        Thread t = Thread.currentThread();
        ThreadLocalMap map = getMap(t);
        if (map != null) {
            ThreadLocalMap.Entry e = map.getEntry(this);
            if (e != null) {
                @SuppressWarnings("unchecked")
                T result = (T)e.value;
                return result;
            }
        }
        return setInitialValue();
    }

第一行,獲取當(dāng)前線程茴她;第二行,通過當(dāng)前線程獲取ThreadLocalMap實(shí)例栈拖。我們跟蹤進(jìn)去看一下

    ThreadLocalMap getMap(Thread t) {
        return t.threadLocals;
    }

進(jìn)入Thread類,發(fā)現(xiàn)t.threadLocals指的是ThreadLocal.ThreadLocalMap没陡,初始值為null。
所以會(huì)調(diào)用setInitialValue函數(shù)

    private T setInitialValue() {
        T value = initialValue();
        Thread t = Thread.currentThread();
        ThreadLocalMap map = getMap(t);
        if (map != null)
            map.set(this, value);
        else
            createMap(t, value);
        return value;
    }

value返回為null索赏,map初始值也為null盼玄,所以會(huì)進(jìn)createMap

    void createMap(Thread t, T firstValue) {
        t.threadLocals = new ThreadLocalMap(this, firstValue);
    }

這里的泛型T是ThreadLocal包裹的泛型對(duì)象,在Looper類中潜腻,ThreadLocal包裹的泛型對(duì)象就是Looper埃儿。那這段代碼的意思就是實(shí)例化了一個(gè)ThreadLocalMap,key為ThreadLocal類融涣,value為L(zhǎng)ooper對(duì)象童番。ThreadLocalMap類在此不做詳細(xì)說明,就理解為一個(gè)類似于鍵值對(duì)存儲(chǔ)的數(shù)據(jù)結(jié)構(gòu)就行威鹿。根據(jù)上面的代碼分析剃斧,這里的value為null,所以最后return的也是null忽你。
顯而易見幼东,如果myLooper返回值是這樣的話Handler就無法工作了。所以我們要提到另外一個(gè)重要的函數(shù)科雳,Looper.prepare

    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() 不能有值根蟹,因此,每個(gè)線程只能調(diào)用一次Looper.prepare函數(shù)糟秘。下面一行是ThreadLocal的set的過程

    public void set(T value) {
        Thread t = Thread.currentThread();
        ThreadLocalMap map = getMap(t);
        if (map != null)
            map.set(this, value);
        else
            createMap(t, value);
    }

這時(shí)候value就是new的一個(gè)Looper對(duì)象了简逮,這樣就做到了以ThreadLocal為key,Looper為value的鍵值對(duì)存進(jìn)了ThreadLocalMap尿赚,ThreadLocalMap與當(dāng)前線程進(jìn)行了綁定散庶。這樣就做到了從當(dāng)前線程中獲取Looper蕉堰。
所以在使用Handler之前,必須執(zhí)行Looper.prepare督赤,之所以我們平時(shí)在主線程中new Handler不需要執(zhí)行嘁灯,是因?yàn)樵贏ctivityThread中已經(jīng)幫我們執(zhí)行了。參看ActivityThread第6525行躲舌。

        Looper.prepareMainLooper();
    public static void prepareMainLooper() {
        prepare(false);
        synchronized (Looper.class) {
            if (sMainLooper != null) {
                throw new IllegalStateException("The main Looper has already been prepared.");
            }
            sMainLooper = myLooper();
        }
    }

到此為止丑婿,構(gòu)造函數(shù)中Looper的分析過程已完成,下面我們需要看另一個(gè)重要類的分析没卸,也就是 mQueue = mLooper.mQueue這行代碼的分析羹奉。mQuene是Handler類中的一個(gè)成員變量,類名為MessageQueue约计,根據(jù)英文翻譯诀拭,可以理解為消息隊(duì)列,但內(nèi)部的數(shù)據(jù)結(jié)構(gòu)并不是隊(duì)列煤蚌,內(nèi)部存儲(chǔ)的都是Message耕挨,從代碼層面來講其實(shí)是單鏈表的結(jié)構(gòu)。mQuene來源于mLooper的成員變量尉桩,初始化的過程在

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

Message類有幾個(gè)重要的屬性筒占,在這里先大概列出來

    public int what;
    public int arg1;
    public int arg2;
    public Object obj;
    long when;
    Bundle data;
    Handler target;
    Message next;

前面打好了基礎(chǔ),接下來蜘犁,我們就可以開始看整個(gè)Handler的運(yùn)行機(jī)制了翰苫。以文章開頭的問題為例,我們來看下代碼的執(zhí)行過程这橙。首先是new Handler奏窑,這個(gè)上文已經(jīng)分析過了,然后里面有一個(gè)handleMessage的覆蓋屈扎,我們看下這里是怎么回調(diào)過來的埃唯。
首先來到ActivityThread的第6541行

        Looper.loop();

這個(gè)loop函數(shù)有50多行,我們從頭分析鹰晨。前幾行是這樣的

        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;

通過throw的異持欤可以看出,在線程中執(zhí)行l(wèi)oop之前必須先prepare并村。
然后拿到了MessageQuene實(shí)例巍实。
再下面幾行,啟動(dòng)了一個(gè)無限for循環(huán)

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

目的就是不斷的從quene中取message哩牍,message為null的時(shí)候退出循環(huán)棚潦。那我們需要看下next函數(shù)的具體實(shí)現(xiàn)。

    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) {

可以看到膝昆,它也有一個(gè)無限for循環(huán)丸边。然后有一個(gè)nativePollOnce函數(shù)的調(diào)用叠必,這是一個(gè)native的函數(shù),也就是c++的實(shí)現(xiàn)妹窖。主要功能是等待纬朝,直到有下一條消息為止。至于為什么死循環(huán)不會(huì)導(dǎo)致應(yīng)用卡死(也就是ANR)骄呼,這個(gè)我們稍后再討論共苛,先繼續(xù)往下看◎烟眩可以看到synchronized關(guān)鍵字修飾了this隅茎,可以看到取消息的過程是線程安全的。定義了兩個(gè)參數(shù)prevMsg表示之前那條消息嫉沽,msg表示下一條消息辟犀。

                    // 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 {

下面一個(gè)do while循環(huán),從鏈表中尾部取出消息绸硕。當(dāng)now小于msg.when堂竟,也就是當(dāng)消息的執(zhí)行之間大于現(xiàn)在的時(shí)間(這個(gè)when是開發(fā)者設(shè)定的消息執(zhí)行時(shí)間點(diǎn),后面也會(huì)解釋)玻佩,下一條message還沒準(zhǔn)備好跃捣,所以需要計(jì)算出還需要多久去喚醒這條消息,也就是nextPollTimeoutMillis 這個(gè)參數(shù)夺蛇。如果當(dāng)前時(shí)間大于next消息的執(zhí)行時(shí)間,則執(zhí)行如下代碼

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

這就是獲取消息的過程酣胀。首先mBlocked設(shè)置為false刁赦,阻塞的tag先關(guān)閉。如果當(dāng)前一條消息不為null闻镶,則把鏈表的最后一條消息賦值給到prevMsg的next消息甚脉,否則把鏈表的最后一條消息賦值給mMessages這個(gè)成員變量。最后把鏈表的最后一條消息刪除并且把當(dāng)前取到的msg置為正在使用铆农,把msg給return給調(diào)用者牺氨。再往后的源碼就是IdelHandler的相關(guān)使用了,我們后面再具體分析墩剖。分析到這猴凹,就有幾個(gè)結(jié)論了,我分別列舉出來
1岭皂、MessageQuene是單鏈表郊霎。
2、每次取出的是鏈表尾部的消息爷绘,當(dāng)這條消息未到執(zhí)行時(shí)間书劝,會(huì)計(jì)算出喚醒時(shí)間進(jìn)入nativePollOnce等待喚醒进倍,否則直接取出msg返回并從鏈表中刪除這條msg。
3购对、當(dāng)鏈表中沒有msg的時(shí)候猾昆,nextPollTimeoutMillis為-1,持續(xù)nativePollOnce等待喚醒骡苞。

分析完了取消息的過程垂蜗,繼續(xù)回到之前的loop函數(shù)。

            try {
                msg.target.dispatchMessage(msg);
            } finally {
                if (traceTag != 0) {
                    Trace.traceEnd(traceTag);
                }
            }

msg.target指的是handler對(duì)象烙如,也就是說looper從MessageQuene取到消息之后會(huì)進(jìn)入到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);
        }
    }

我們假定不設(shè)置callBack,則最終進(jìn)入到了handleMessage亚铁,這就是new Handler里面handleMessage函數(shù)的執(zhí)行過程蝇刀。
接下去是sendMessage的過程分析。通過跟蹤sendMessage相關(guān)的源代碼徘溢,可以發(fā)現(xiàn)不管是sendMessage還是sendEmptyMessage都最終會(huì)進(jìn)入到如下函數(shù)

    public final boolean sendMessageDelayed(Message msg, long delayMillis)
    {
        if (delayMillis < 0) {
            delayMillis = 0;
        }
        return sendMessageAtTime(msg, SystemClock.uptimeMillis() + delayMillis);
    }

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

其中SystemClock.uptimeMillis() 是表示系統(tǒng)開機(jī)到當(dāng)前的時(shí)間總數(shù)吞琐,單位是毫秒,但是然爆,當(dāng)系統(tǒng)進(jìn)入深度睡眠(CPU休眠站粟、屏幕休眠、設(shè)備等待外部輸入)時(shí)間就會(huì)停止曾雕,但是不會(huì)受到時(shí)鐘縮放奴烙、空閑或者其他節(jié)能機(jī)制的影響。官方解釋如下

    /**
     * Returns milliseconds since boot, not counting time spent in deep sleep.
     *
     * @return milliseconds of non-sleep uptime since boot.
     */
    @CriticalNative
    native public static long uptimeMillis();

下面的enqueueMessage函數(shù)就是MessageQuene中Message的添加過程剖张。
先截取一些代碼看一下

        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 {

又看到了synchronized關(guān)鍵字切诀,所以enqueueMessage也是線程安全的。通過精讀上方代碼搔弄,我們可以得出如下結(jié)論
1幅虑、p==null表示第一次sendMessage,則鏈表中只有這個(gè)消息顾犹。
2倒庵、when==0表示handler執(zhí)行了sendMessageAtFrontOfQueue這個(gè)函數(shù),官方文檔標(biāo)注的是說把消息放在消息隊(duì)列的最前方炫刷,在單鏈表中指的是放到鏈表的尾部擎宝。
3、when<p.when指的是執(zhí)行時(shí)間小于鏈表尾部消息的執(zhí)行時(shí)間浑玛,則把這條消息置于鏈表尾部认臊。
當(dāng)p!=null&&when>0&&when>p.when的時(shí)候的執(zhí)行流程如下

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

啟動(dòng)一個(gè)死循環(huán),直到p==null或者when<p.when的時(shí)候退出锄奢,目的是為了找到鏈表中when值大于當(dāng)前when的消息失晴。然后把p賦值給msg.next剧腻,msg賦值給pre.next。很顯然涂屁,這就是往鏈表中插入了一個(gè)元素书在。when值越小,越接近鏈表尾部拆又。分析至此儒旬,我們又可以得出結(jié)論了。
1帖族、結(jié)論前提:p!=null&&when>0&&when>p.when
2栈源、MessageQuene中每次進(jìn)入新消息,都需要根據(jù)msg.when進(jìn)行排序竖般,msg.when越小甚垦,越接近鏈表尾部。
至此為止涣雕,源碼層面的Handler機(jī)制全部分析完畢艰亮。
接下來,我們解答文出現(xiàn)的幾個(gè)問題:
1挣郭、為什么要有l(wèi)ooper死循環(huán)迄埃。
因?yàn)镴ava的Main函數(shù)執(zhí)行完就退出了。對(duì)于Android這樣的GUI程序兑障,肯定不能執(zhí)行完就退出侄非,所以引入了死循環(huán),讓線程一直執(zhí)行下去流译。
1逞怨、Looper.loop是一個(gè)死循環(huán),為什么不會(huì)卡死Android UI先蒋,為什么不會(huì)導(dǎo)致ANR。
Android 所有的 UI 刷新和生命周期的回調(diào)都是由 Handler消息機(jī)制完成的宛渐,就是說 UI 刷新和生命周期的回調(diào)都是依賴 Looper 里面的死循環(huán)完成的竞漾。其中,在UI的渲染過程中窥翩,會(huì)調(diào)用到如下代碼

    void scheduleTraversals() {
        if (!mTraversalScheduled) {
            mTraversalScheduled = true;
            mTraversalBarrier = mHandler.getLooper().getQueue().postSyncBarrier();
            mChoreographer.postCallback(
                    Choreographer.CALLBACK_TRAVERSAL, mTraversalRunnable, null);
            if (!mUnbufferedInputDispatch) {
                scheduleConsumeBatchedInput();
            }
            notifyRendererOfFramePending();
            pokeDrawLockIfNeeded();
        }
    }

其中有這么一行mHandler.getLooper().getQueue().postSyncBarrier();這就是說渲染view的過程也用到了handler機(jī)制业岁,并且渲染message的優(yōu)先級(jí)高于普通message。在Android各大組件的生命周期中寇蚊,利用了ActivityThread類中的H類來處理笔时,也是Handler機(jī)制。
2仗岸、nativePollOnce是什么允耿。
參考linux epoll
3借笙、文章開頭的問題答案是什么。
第二個(gè)函數(shù)會(huì)等待第一個(gè)函數(shù)執(zhí)行完了再執(zhí)行较锡。根據(jù)以上分析业稼,dispatchMessage發(fā)生在loop for循環(huán)中,消息是一條條取出的蚂蕴,msg.when小的排在鏈表尾部低散,會(huì)優(yōu)先被取出然后dispatch。handleMessage執(zhí)行完了之后骡楼,才會(huì)取下一條消息執(zhí)行熔号。

下一篇預(yù)告:ConcurrentHashMap源碼分析

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個(gè)濱河市鸟整,隨后出現(xiàn)的幾起案子引镊,更是在濱河造成了極大的恐慌,老刑警劉巖吃嘿,帶你破解...
    沈念sama閱讀 217,657評(píng)論 6 505
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件祠乃,死亡現(xiàn)場(chǎng)離奇詭異,居然都是意外死亡兑燥,警方通過查閱死者的電腦和手機(jī)亮瓷,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 92,889評(píng)論 3 394
  • 文/潘曉璐 我一進(jìn)店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來降瞳,“玉大人嘱支,你說我怎么就攤上這事≌跫ⅲ” “怎么了除师?”我有些...
    開封第一講書人閱讀 164,057評(píng)論 0 354
  • 文/不壞的土叔 我叫張陵,是天一觀的道長(zhǎng)扔枫。 經(jīng)常有香客問我汛聚,道長(zhǎng),這世上最難降的妖魔是什么短荐? 我笑而不...
    開封第一講書人閱讀 58,509評(píng)論 1 293
  • 正文 為了忘掉前任倚舀,我火速辦了婚禮,結(jié)果婚禮上忍宋,老公的妹妹穿的比我還像新娘痕貌。我一直安慰自己,他們只是感情好糠排,可當(dāng)我...
    茶點(diǎn)故事閱讀 67,562評(píng)論 6 392
  • 文/花漫 我一把揭開白布舵稠。 她就那樣靜靜地躺著,像睡著了一般。 火紅的嫁衣襯著肌膚如雪哺徊。 梳的紋絲不亂的頭發(fā)上室琢,一...
    開封第一講書人閱讀 51,443評(píng)論 1 302
  • 那天,我揣著相機(jī)與錄音唉工,去河邊找鬼研乒。 笑死,一個(gè)胖子當(dāng)著我的面吹牛淋硝,可吹牛的內(nèi)容都是我干的雹熬。 我是一名探鬼主播,決...
    沈念sama閱讀 40,251評(píng)論 3 418
  • 文/蒼蘭香墨 我猛地睜開眼谣膳,長(zhǎng)吁一口氣:“原來是場(chǎng)噩夢(mèng)啊……” “哼竿报!你這毒婦竟也來了?” 一聲冷哼從身側(cè)響起继谚,我...
    開封第一講書人閱讀 39,129評(píng)論 0 276
  • 序言:老撾萬榮一對(duì)情侶失蹤烈菌,失蹤者是張志新(化名)和其女友劉穎,沒想到半個(gè)月后花履,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體芽世,經(jīng)...
    沈念sama閱讀 45,561評(píng)論 1 314
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 37,779評(píng)論 3 335
  • 正文 我和宋清朗相戀三年诡壁,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了济瓢。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點(diǎn)故事閱讀 39,902評(píng)論 1 348
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡妹卿,死狀恐怖旺矾,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情夺克,我是刑警寧澤箕宙,帶...
    沈念sama閱讀 35,621評(píng)論 5 345
  • 正文 年R本政府宣布,位于F島的核電站铺纽,受9級(jí)特大地震影響柬帕,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜狡门,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,220評(píng)論 3 328
  • 文/蒙蒙 一陷寝、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧融撞,春花似錦盼铁、人聲如沸粗蔚。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,838評(píng)論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至致扯,卻和暖如春肤寝,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背抖僵。 一陣腳步聲響...
    開封第一講書人閱讀 32,971評(píng)論 1 269
  • 我被黑心中介騙來泰國(guó)打工鲤看, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留,地道東北人耍群。 一個(gè)月前我還...
    沈念sama閱讀 48,025評(píng)論 2 370
  • 正文 我出身青樓义桂,卻偏偏與公主長(zhǎng)得像,于是被迫代替她去往敵國(guó)和親蹈垢。 傳聞我的和親對(duì)象是個(gè)殘疾皇子慷吊,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 44,843評(píng)論 2 354