Android Handler運行機制Java層源碼分析

1. Handler的使用

    mHandler = new Handler(){
        @Override
        public void handleMessage(Message msg) {
            super.handleMessage(msg);
        }
    };
    Message msg = mHandler.obtainMessage(what);
    mHandler.sendMessage(msg);

從上述代碼可知抡砂,我們使用Handler的流程:

    創(chuàng)建Handler -> 獲取消息對象 -> 發(fā)送消息 -> 處理消息

2. Handler的創(chuàng)建

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

構建函數中比較重要的是looper(Looper)對象,mQueue(MessageQueue)對象是與looper相關聯(lián)的肿嘲,mCallbackmAsynchronous后續(xù)會講解偶芍。

3. Looper的來歷

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

構建函數中設置當前線程對象到mThread變量中贞铣,并創(chuàng)建了一個MessageQueue對象寥茫,其創(chuàng)建過程我們稍后分析。心細的讀者可能已經發(fā)現(xiàn)這是個私有構造函數边锁,那么肯定是在類內部進行調用的姑食,搜索發(fā)現(xiàn)其調用函數為:

    public static void prepare() {
        prepare(true);
    }

    // 目前quitAllowed應該僅在主線程中被設置成false,自定義線程中均為true
    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));
    }

Looper#prepare方法會創(chuàng)建Looper對象茅坛,并且某一線程僅能創(chuàng)建一個Looper對象音半,然后將創(chuàng)建的對象設置為線程安全變量。那么這個函數在哪里調用咧贡蓖?既然跟線程相關曹鸠,那么大膽猜測是在線程中調用的,而Android中最主要的線程是主線程摩梧,我們一開始的“Handler的基本使用”中并沒有創(chuàng)建Looper物延,卻可以使用Handler,那么Android主線程中必然在生成的時候就新建了一個Looper仅父,查看Looper源碼時會發(fā)現(xiàn)的確含有一個特殊的Looper(即:sMainLooper),這個特殊Looper對象通過調用Looper#prepareMainLooper()創(chuàng)建的浑吟,我們可以去ActivityThread中去看是如何實現(xiàn)的:

    public static void main(String[] args) {
        //···
        Looper.prepareMainLooper();
        //···
        Looper.loop();
        //···
    }

上述代碼已去掉無關代碼笙纤,至此我們就了解了Looper的來歷,但之后調用的Looper#loop()是個什么鬼?這個主要是進入一個不斷從MessageQueue中獲取消息進行處理的無限循環(huán)组力,在分析這個循環(huán)之前我們要先來分析下MessageQueue是個什么鬼省容。

4. MessageQueue是什么鬼

    mQueue = new MessageQueue(quitAllowed);

上面創(chuàng)建Looper的時新建了一個MessageQueue對象,看下它的構造函數:

    MessageQueue(boolean quitAllowed) {
        mQuitAllowed = quitAllowed;
        mPtr = nativeInit();
    }

    private native static long nativeInit();

WFK燎字,你就給我看這個P冉贰!候衍!C++學渣表示強烈抗議笼蛛。對于Native感興趣的小伙伴可以自行查看源碼,我跟著老羅的文章:Android應用程序消息處理機制(Looper蛉鹿、Handler)分析稍微感受了下源碼的邏輯滨砍,nativeInit就是在native層創(chuàng)建了一個NativeMessageQueue及一個Looper對象,而native層的Looper對象利用管道機制來監(jiān)控文件,從而可以利用epoll機制實現(xiàn)MessageQueue的等待和喚醒惋戏,這個在MessageQueue#next()會進一步分析领追。至此,MessageQueue對象就被創(chuàng)建出來了(由于MessageQueue的構建函數僅有包訪問權限响逢,因此正常情況下我們無需關心MessageQueue的創(chuàng)建)绒窑,然后我們來看下Looper#loop()到底在做什么。

5. Looper#loop()到底正在做什么

    public static void loop() {
        final Looper me = myLooper();
        if (me == null) {
            throw new RuntimeException("Looper.prepare() wasn't called on this thread.");
        }
        final MessageQueue queue = me.mQueue;
        //···省略無關代碼
        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();
        }
    }

Looper#loop()方法只有當Looper被創(chuàng)建出來之后方可調用舔亭,主要包括一個無限循環(huán):從MessageQueue中獲取消息進行處理些膨,然后回收處理完的消息。我們先來看MessageQueue#next():

    Message next() {
        // 在Looper#loop()中我們知道返回空消息會退出loop()中的無限循環(huán)
        // 當調用MessageQueue#quit(boolean)時會調用nativeDestory()銷毀MessageQueue分歇,將ptr置為0
        final long ptr = mPtr;
        if (ptr == 0) {
            return null;
        }

        int pendingIdleHandlerCount = -1; // 僅在第一次調用時為-1
        int nextPollTimeoutMillis = 0;
        for (;;) {
            if (nextPollTimeoutMillis != 0) {
                Binder.flushPendingCommands();
            }

            /**
            * 這個調用跟上面提到native層中Looper的epoll機制相關傀蓉,用于等待可處理的消息
            * nextPollTimeoutMillis < 0 : 進入無限空閑等待,直到有新消息喚醒
            * nextPollTimeoutMillis = 0 : 不等待
            * nextPollTimeoutMillis > 0 : 進入空閑等待职抡,直到有新消息喚醒或者nextPollTimeoutMillis超時
            **/
            nativePollOnce(ptr, nextPollTimeoutMillis);

            synchronized (this) {
                final long now = SystemClock.uptimeMillis();
                Message prevMsg = null;
                Message msg = mMessages;
                // 注:MessageQueue管理的消息是一個消息鏈表葬燎,后續(xù)Message中會詳細分析
                if (msg != null && msg.target == null) {
                    /**
                    * msg.target為空是一類特殊消息(柵欄消息),用于阻塞所有同步消息缚甩,但是對異步消息沒有影響谱净,
                    * 后續(xù)會詳細分析。在這個前提下擅威,當頭部是特殊消息時需要往后找是否有異步消息
                    */
                    do {
                        prevMsg = msg;
                        msg = msg.next;
                    } while (msg != null && !msg.isAsynchronous());
                }
                if (msg != null) {
                    // 找到消息
                    if (now < msg.when) {
                        // 消息的觸發(fā)時間在當前時間之后壕探,于是計算出需要等待的時間,準備進入有限空閑等待
                        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;
                        if (false) Log.v("MessageQueue", "Returning message: " + msg);
                        return msg;
                    }
                } else {
                    // 沒找到消息,準備進入無限空閑等待
                    nextPollTimeoutMillis = -1;
                }

                // 沒有可處理的消息厉熟,并且消息隊列已經退出导盅,則返回空消息讓loop退出
                if (mQuitting) {
                    dispose();
                    return null;
                }

                // 當mMessages為空或者mMessages的處理時間在當前時間之后(注意柵欄消息的特殊情況)時,
                // 并且pendingIdleHandlerCount沒有在此處初始化過揍瑟,
                // 則設置pendingIdleHandlerCount為IdleHandler的數量白翻,IdleHandler后續(xù)詳細說明。
                if (pendingIdleHandlerCount < 0
                        && (mMessages == null || now < mMessages.when)) {
                    pendingIdleHandlerCount = mIdleHandlers.size();
                }
                if (pendingIdleHandlerCount <= 0) {
                    // 無空閑處理器绢片,阻塞隊列滤馍,進入空閑等待
                    mBlocked = true;
                    continue;
                }

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

            // 調用空閑處理器邏輯,此處代碼僅調用一次
            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("MessageQueue", "IdleHandler threw exception", t);
                }

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

            // 設置為0保證空閑處理器代碼僅調用一次
            pendingIdleHandlerCount = 0;

            // 在處理空閑處理器的時候可能已經有可處理的消息底循,因此無需等待
            nextPollTimeoutMillis = 0;
        }
    }

由于這個函數是消息獲取的關鍵巢株,因此是無刪減版。而相關說明直接注釋在代碼中此叠,主要目的在于建議小伙伴們看源碼纯续。我相信讀完源碼應該對獲取消息的機制有了比較完整的了解随珠,當loopMessageQueue中獲取到消息便可以進行消息處理并在處理后回收該消息,具體等我們分析完消息的發(fā)送之后再來看猬错。

6. Handler#sendMessage中的消息怎么獲取到

    public final Message obtainMessage(){
        return Message.obtain(this);
    }

直接調用Message#obtain(Handler):

    public static Message obtain(Handler h) {
        Message m = obtain();
        m.target = h;
        return m;
    }

直接調用Message#obtain() - -!:

    public static Message obtain() {
        synchronized (sPoolSync) {
            if (sPool != null) {
                Message m = sPool;
                sPool = m.next;
                m.next = null;
                m.flags = 0; // clear in-use flag
                sPoolSize--;
                return m;
            }
        }
        return new Message();
    }

這里會先從回收池中取消息窗看,如果沒有就新創(chuàng)建一條消息;回收池是一個消息鏈表倦炒,sPoolSync是同步符號显沈,sPool是鏈表頭,sPoolSize是回收池中可用消息數逢唤,最大限制為MAX_POOL_SIZE(默認為50)拉讯。這些都沒有太大可分析性,我們來看看Message的結構:

    public int what;    // 消息碼鳖藕,帶Handler命名空間魔慷,因此不同Handler中相同消息碼不沖突
    public int arg1;    // 整數數據
    public int arg2;    // 整數數據
    public Object obj;  // 任意對象,當利用Messenger進行跨進程傳遞時需要繼承自Parcelable

    public Messenger replyTo;   // Messenger對象實現(xiàn)跨進程消息傳遞
    public int sendingUid = -1; // 跨進程是標記消息來源的Uid

    /**
    * flags可設置消息是否在使用以及是否異步
    * FLAG_IN_USE = 1 << 0著恩,該標記只有在創(chuàng)建或obtain時才會清除院尔,此時方可修改消息的相關數據及進行發(fā)送
    * FLAG_ASYNCHRONOUS = 1 << 1,標記該消息為異步消息喉誊,不受柵欄消息的影響
    **/
        /*package*/ int flags;

        /*package*/ long when;  // 消息執(zhí)行時間邀摆,采用SystemClock#uptimeMillis()時間base
        /*package*/ Bundle data;    // 消息的數據
        /*package*/ Handler target; // 消息對應的Handler
        /*package*/ Runnable callback; // 消息對應的回調,具體參看下文中消息處理一節(jié)

        /*package*/ Message next; // 形成消息鏈表伍茄,以在MessageQueue以及消息回收池中使用

至此栋盹,消息的來源以及消息的結構分析完畢,其中flagsMessag自己管理敷矫,datagetData例获、peekData以及setData進行管理,targetcallbackHandler中相關獲取或發(fā)送消息的接口管理曹仗。獲取到消息之后躏敢,便可以調用Handler的消息發(fā)送接口進行發(fā)送,那是如何進入MessageQueue的咧?

7. Handler#sendMessage中的消息怎么放入消息隊列

Handler中的消息發(fā)送接口除了Handler#sendMessageAtFrontOfQueue(Message)均會調用Handler#sendMessageAtTime(Message)讥脐,而這兩個接口最終調用了Handler#enqueueMessage(MessageQueue, Message, long):

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

根據HandlermAsynchronous屬性設置消息的異步屬性遭居,最后調用MessageQueue#enqueueMessage(Message, long):

    // 插入成功返回true,否則返回false
    boolean enqueueMessage(Message msg, long when) {
        // Handler中不允許發(fā)送target為空的消息旬渠,空消息為特殊消息(柵欄消息)
        if (msg.target == null) {
            throw new IllegalArgumentException("Message must have a target.");
        }
        // 不允許發(fā)送狀態(tài)為使用中的消息
        if (msg.isInUse()) {
            throw new IllegalStateException(msg + " This message is already in use.");
        }

        synchronized (this) {
            if (mQuitting) {
                // 不允許發(fā)送消息給已退出的消息隊列
                IllegalStateException e = new IllegalStateException(
                        msg.target + " sending message to a Handler on a dead thread");
                Log.w("MessageQueue", 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) {
                // 消息觸發(fā)時間最早俱萍,直接插在鏈表頭部,如果當前隊列阻塞則喚醒消息隊列的等待告丢,見MessageQueue#next
                msg.next = p;
                mMessages = msg;
                needWake = mBlocked;
            } else {
                // 將消息插入到鏈表中間枪蘑,如果鏈表頭是柵欄消息并且該消息是觸發(fā)時間最早的異步消息則需要進行喚醒
                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;
                prev.next = msg;
            }

            if (needWake) {
                nativeWake(mPtr);
            }
        }
        return true;
    }

建議查看源碼,相關說明已注釋在代碼中。把新消息放入隊列岳颇,并在必要的時候喚醒消息隊列進行處理照捡,從而就回到上述MessageQueue#next的邏輯中,然后在有可處理消息的時候將消息發(fā)送到Looper#loop中進行處理及回收话侧。

8. Message的處理及回收

當消息被返回到loop中時栗精,調用:

    msg.target.dispatchMessage(msg);
    msg.recycleUnchecked();

target就是Handler,于是調用:

    public void dispatchMessage(Message msg) {
        if (msg.callback != null) {
            handleCallback(msg);
        } else {
            if (mCallback != null) {
                if (mCallback.handleMessage(msg)) {
                    return;
                }
            }
            handleMessage(msg);
        }
    }

上述代碼表明:當消息本身回調不為空時則由消息本身回調處理該消息瞻鹏;當HandlermCallback不為空時則由HandlermCallback處理消息悲立;否則則由Handler中的鉤子handleMessage進行處理。消息處理完了之后需要將消息回收:

    void recycleUnchecked() {
        // 標記為使用中新博,清除所有數據
        flags = FLAG_IN_USE;
        what = 0;
        arg1 = 0;
        arg2 = 0;
        obj = null;
        replyTo = null;
        sendingUid = -1;
        when = 0;
        target = null;
        callback = null;
        data = null;

        // 放入消息回收池
        synchronized (sPoolSync) {
            if (sPoolSize < MAX_POOL_SIZE) {
                next = sPool;
                sPool = this;
                sPoolSize++;
            }
        }
    }

至此薪夕,整個Handler的運行機制(創(chuàng)建Handler -> 獲取消息對象 -> 發(fā)送消息 -> 處理消息)就分析完了。

9. 一些其他補充

9.1 柵欄消息

柵欄消息target為空的特殊消息赫悄,用于延遲MessageQueue中所有指定時間之后的同步消息原献,異步消息則仍可執(zhí)行。發(fā)送和移除柵欄消息必須成對出現(xiàn)涩蜘,否則可能導致MessageQueue被掛起嚼贡。
其發(fā)送移除接口在Looper中:

    public int postSyncBarrier() {
        return mQueue.enqueueSyncBarrier(SystemClock.uptimeMillis());
    }

    public void removeSyncBarrier(int token) {
        mQueue.removeSyncBarrier(token);
    }

調用了MessageQueue#enqueueSyncBarrier(long):

    int enqueueSyncBarrier(long when) {
        // 創(chuàng)建一個target為空的特殊消息,并根據when插入MessageQueue中合適的位置
        // 無需喚醒因為柵欄消息的目的在于阻塞消息的執(zhí)行
        synchronized (this) {
            final int token = mNextBarrierToken++;
            final Message msg = Message.obtain();
            msg.markInUse();
            msg.when = when;
            msg.arg1 = token;

            Message prev = null;
            Message p = mMessages;
            if (when != 0) {
                while (p != null && p.when <= when) {
                    prev = p;
                    p = p.next;
                }
            }
            if (prev != null) {
                msg.next = p;
                prev.next = msg;
            } else {
                msg.next = p;
                mMessages = msg;
            }
            return token;
        }
    }

必須成對出現(xiàn)的MessageQueue#removeSyncBarrier(token)同诫,其中tokenenqueueSyncBarrier返回:

    void removeSyncBarrier(int token) {
        // 移除token對應的柵欄消息粤策,并在必要的時候進行喚醒
        synchronized (this) {
            Message prev = null;
            Message p = mMessages;
            while (p != null && (p.target != null || p.arg1 != token)) {
                prev = p;
                p = p.next;
            }
            if (p == null) {
                throw new IllegalStateException("The specified message queue synchronization "
                        + " barrier token has not been posted or has already been removed.");
            }
            final boolean needWake;
            if (prev != null) {
                prev.next = p.next;
                needWake = false;
            } else {
                mMessages = p.next;
                needWake = mMessages == null || mMessages.target != null;
            }
            p.recycleUnchecked();

            if (needWake && !mQuitting) {
                nativeWake(mPtr);
            }
        }
    }

有沒有覺得,so easy - -误窖!

9.2 空閑處理器(IdleHandler)

IdleHandler定義在MessageQueue中:

    public static interface IdleHandler {
        // 返回true表示保持在MessageQueue的mIdleHandlers中
        boolean queueIdle();
    }

具體調用時機見MessageQueue#next中的分析叮盘。

9.3 Handler的相關接口介紹

獲取消息(帶不同參數):

    final Message obtainMessage()
    final Message obtainMessage(int what)
    final Message obtainMessage(int what, int arg1, int arg2)
    final Message obtainMessage(int what, Object obj)
    final Message obtainMessage(int what, int arg1, int arg2, Object obj)

發(fā)送消息:

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

發(fā)送帶Callback的消息:

    post(Runnable r)
    final boolean postAtFrontOfQueue(Runnable r)
    final boolean postAtTime(Runnable r, Object token, long uptimeMillis)
    final boolean postAtTime(Runnable r, long uptimeMillis)
    final boolean postDelayed(Runnable r, long delayMillis)

移除消息:

    final void removeCallbacks(Runnable r)
    final void removeCallbacks(Runnable r, Object token)
    final void removeCallbacksAndMessages(Object token)
    final void removeMessages(int what)
    final void removeMessages(int what, Object object)

9.4 HandlerThread類

HandlerThread類是Handler的一個輔助類,當調用HandlerThread#start()之后霹俺,會創(chuàng)建一個帶LooperThread

    public void run() {
        mTid = Process.myTid();
        Looper.prepare();
        synchronized (this) {
            mLooper = Looper.myLooper();
            notifyAll();
        }
        Process.setThreadPriority(mPriority);
        onLooperPrepared();
        Looper.loop();
        mTid = -1;
    }

這樣柔吼,我們使用非主線程Handler的時候便比較簡單了:

    HandlerThread t = new HandlerThread(TAG);
    t.start();
    mHandler = new Handler(t.getLooper(), new Handler.Callback() {
        @Override
        public boolean handleMessage(Message msg) {
             // TODO: 處理消息
        }
    }); 

10. 參考文獻

  1. Android應用程序消息處理機制(Looper、Handler)分析 - - 羅升陽
最后編輯于
?著作權歸作者所有,轉載或內容合作請聯(lián)系作者
  • 序言:七十年代末丙唧,一起剝皮案震驚了整個濱河市愈魏,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌想际,老刑警劉巖培漏,帶你破解...
    沈念sama閱讀 210,914評論 6 490
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場離奇詭異胡本,居然都是意外死亡牌柄,警方通過查閱死者的電腦和手機,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 89,935評論 2 383
  • 文/潘曉璐 我一進店門侧甫,熙熙樓的掌柜王于貴愁眉苦臉地迎上來珊佣,“玉大人蹋宦,你說我怎么就攤上這事≈涠停” “怎么了冷冗?”我有些...
    開封第一講書人閱讀 156,531評論 0 345
  • 文/不壞的土叔 我叫張陵,是天一觀的道長虫碉。 經常有香客問我贾惦,道長,這世上最難降的妖魔是什么敦捧? 我笑而不...
    開封第一講書人閱讀 56,309評論 1 282
  • 正文 為了忘掉前任须板,我火速辦了婚禮,結果婚禮上兢卵,老公的妹妹穿的比我還像新娘习瑰。我一直安慰自己,他們只是感情好秽荤,可當我...
    茶點故事閱讀 65,381評論 5 384
  • 文/花漫 我一把揭開白布甜奄。 她就那樣靜靜地躺著,像睡著了一般窃款。 火紅的嫁衣襯著肌膚如雪课兄。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 49,730評論 1 289
  • 那天晨继,我揣著相機與錄音烟阐,去河邊找鬼。 笑死紊扬,一個胖子當著我的面吹牛蜒茄,可吹牛的內容都是我干的。 我是一名探鬼主播餐屎,決...
    沈念sama閱讀 38,882評論 3 404
  • 文/蒼蘭香墨 我猛地睜開眼檀葛,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了腹缩?” 一聲冷哼從身側響起屿聋,我...
    開封第一講書人閱讀 37,643評論 0 266
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎藏鹊,沒想到半個月后胜臊,有當地人在樹林里發(fā)現(xiàn)了一具尸體,經...
    沈念sama閱讀 44,095評論 1 303
  • 正文 獨居荒郊野嶺守林人離奇死亡伙判,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內容為張勛視角 年9月15日...
    茶點故事閱讀 36,448評論 2 325
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發(fā)現(xiàn)自己被綠了黑忱。 大學時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片宴抚。...
    茶點故事閱讀 38,566評論 1 339
  • 序言:一個原本活蹦亂跳的男人離奇死亡勒魔,死狀恐怖,靈堂內的尸體忽然破棺而出菇曲,到底是詐尸還是另有隱情冠绢,我是刑警寧澤,帶...
    沈念sama閱讀 34,253評論 4 328
  • 正文 年R本政府宣布常潮,位于F島的核電站弟胀,受9級特大地震影響,放射性物質發(fā)生泄漏喊式。R本人自食惡果不足惜孵户,卻給世界環(huán)境...
    茶點故事閱讀 39,829評論 3 312
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望岔留。 院中可真熱鬧夏哭,春花似錦、人聲如沸献联。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,715評論 0 21
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽里逆。三九已至进胯,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間原押,已是汗流浹背胁镐。 一陣腳步聲響...
    開封第一講書人閱讀 31,945評論 1 264
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留班眯,地道東北人希停。 一個月前我還...
    沈念sama閱讀 46,248評論 2 360
  • 正文 我出身青樓,卻偏偏與公主長得像署隘,于是被迫代替她去往敵國和親宠能。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當晚...
    茶點故事閱讀 43,440評論 2 348

推薦閱讀更多精彩內容