Handler消息通信機(jī)制完全解析篇

本文有借鑒過(guò)網(wǎng)絡(luò)上優(yōu)秀的文章缺亮,加上自己的總結(jié)。

為什么要使用Handler?

為了保證Android的UI操作是線程安全的涵叮,Android規(guī)定只允許UI線程修改UI組件惭蹂。
但在實(shí)際開發(fā)中,必然會(huì)遇到多個(gè)線程并發(fā)操作UI組件的時(shí)候割粮,這將導(dǎo)致UI操作的線程不安全盾碗。
問(wèn)題在于,如何同時(shí)滿足:

  1. 保證線程安全
  2. 使多個(gè)線程并發(fā)操作UI組件
    Handler消息機(jī)制可以解決這個(gè)問(wèn)題舀瓢。

是否熟悉以下相關(guān)概念廷雅?YES的話本段可略過(guò)!

  • 主線程
    定義:程序第一次啟動(dòng)時(shí)京髓,Android會(huì)同時(shí)啟動(dòng)一條主線程(MainThread)
    作用:主線程主要負(fù)責(zé)處理與UI相關(guān)的事件航缀,所以主線程又叫UI線程
  • Message
    定義:Handler接收和處理的消息對(duì)象,可以理解為封裝了某些數(shù)據(jù)的對(duì)象
    使用:后臺(tái)線程處理完數(shù)據(jù)后需要更新UI堰怨,可發(fā)送一條包含更新信息的Message給UI線程
  • Message Queue
    定義:消息隊(duì)列
    作用:用來(lái)存放通過(guò)Handler發(fā)送的消息芥玉,按照先進(jìn)先出的順序排列
  • Handler
    定義:Handler是Message的主要處理者
    作用:
    1.負(fù)責(zé)將Message添加到消息隊(duì)列中
    2.處理Looper分派過(guò)來(lái)的Message
  • Looper
    定義:循環(huán)器,不斷的取出MessageQueue中的消息并傳遞給Handler
    作用:循環(huán)取出MessageQueue的Message备图,將取出的Message交付給相應(yīng)的Handler
    PS:每個(gè)線程只能擁有一個(gè)Looper灿巧,但一個(gè)Looper可以和多個(gè)線程的Handler綁定,也就是說(shuō)多個(gè)線程可以往一個(gè)Looper所持有的MessageQueue中發(fā)送消息揽涮,這給我們提供了線程之間通信的可能抠藕。

Handler工作流程

  • 異步通信的準(zhǔn)備

1.Looper對(duì)象的創(chuàng)建和實(shí)例化
Looper.prepare()
Looper.loop()

2.MessageQueue隊(duì)列的創(chuàng)建
Looper.prepare()->new Looper()
一個(gè)線程只會(huì)有一個(gè)Looper實(shí)例,同時(shí)一個(gè)Looper實(shí)例也只有會(huì)創(chuàng)建一個(gè)MessageQueue蒋困。

3.Handler實(shí)例化
Handler是和線程綁定在一起的盾似,初始化Handler的時(shí)候一般通過(guò)指定Looper對(duì)象從而綁定相應(yīng)線程,即給Handler指定Looper對(duì)象相當(dāng)于綁定到了Looper對(duì)象所在的線程中家破。Handler的消息處理回調(diào)會(huì)在那個(gè)線程中執(zhí)行颜说。

實(shí)例化Handler的方法:
1) 通過(guò)Looper.mylooper()得到當(dāng)前線程的Looper對(duì)象,或通過(guò)Loop.getMainLooper()獲得當(dāng)前進(jìn)程的主線程的Looper對(duì)象汰聋。
2)不指定Looper對(duì)象门粪,這個(gè)Handler綁定到創(chuàng)建這個(gè)線程的線程上,消息處理回調(diào)也就在在創(chuàng)建線程中執(zhí)行烹困。
當(dāng)Handler初始化時(shí)玄妈,可通過(guò)構(gòu)造方法自動(dòng)關(guān)聯(lián)Looper和相應(yīng)的MessageQueue。

  • 消息發(fā)送
    Handler將消息發(fā)送到消息隊(duì)列中

  • 消息循環(huán)
    Looper執(zhí)行Looper.loop()進(jìn)入消息循環(huán)髓梅,在循環(huán)過(guò)程中不斷從該MessageQueue取出消息拟蜻,并將取出的消息派發(fā)給創(chuàng)建該消息的Handler...

  • 消息處理
    通過(guò)Handler的dispatchMessage(msg)方法,即回調(diào)handleMessage(msg)處理消息枯饿。如果時(shí)Handler的post方法發(fā)送的消息酝锅,則會(huì)在對(duì)應(yīng)的run()方法中處理回調(diào)。

源碼分析

Looper.prepare()是在做什么奢方?

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

    private static void prepare(boolean quitAllowed) {
        //一個(gè)線程只能持有一個(gè)Looper實(shí)例搔扁,sThreadLocal保存線程持有的looper對(duì)象
        if (sThreadLocal.get() != null) {
            throw new RuntimeException("Only one Looper may be created per thread");
        }

        //sThreadLocal是一個(gè)ThreadLocal變量爸舒,用于在一個(gè)線程中存儲(chǔ)變量,這里L(fēng)ooper變量就存放在ThreadLocal里
        sThreadLocal.set(new Looper(quitAllowed));
    }

Looper的構(gòu)造方法在做什么稿蹲?

    private Looper(boolean quitAllowed) {
        //創(chuàng)建Looper時(shí)扭勉,會(huì)自動(dòng)創(chuàng)建一個(gè)與之匹配的MessageQueue
        mQueue = new MessageQueue(quitAllowed);
        mThread = Thread.currentThread();
    }

Looper.loop()方法作用是什么?

/**
     * Run the message queue in this thread. Be sure to call
     * {@link #quit()} to end the loop.
     */
    public static void loop() {
        //myLooper()方法是返回sThreadLocal存儲(chǔ)的Looper實(shí)例
        final Looper me = myLooper();

        //me==null就會(huì)拋出異常苛聘,說(shuō)明looper對(duì)象沒有被創(chuàng)建涂炎,
        //也就是說(shuō)loop方法必須在prepare方法之后運(yùn)行,消息循環(huán)必須要先在線程中創(chuàng)建Looper實(shí)例
        if (me == null) {
            throw new RuntimeException("No Looper; Looper.prepare() wasn't called on this thread.");
        }

        //獲取Looper實(shí)例中的消息隊(duì)列mQueue
        final MessageQueue queue = me.mQueue;

        // Make sure the identity of this thread is that of the local process,
        // and keep track of what that identity token actually is.
        Binder.clearCallingIdentity();
        final long ident = Binder.clearCallingIdentity();

        for (;;) {
            //next()方法用于取出消息隊(duì)列中的消息设哗,如果取出的消息為空唱捣,則線程阻塞
            Message msg = queue.next(); // might block
            if (msg == null) {
                // No message indicates that the message queue is quitting.
                return;
            }

            // This must be in a local variable, in case a UI event sets the logger
            Printer logging = me.mLogging;
            if (logging != null) {
                logging.println(">>>>> Dispatching to " + msg.target + " " +
                        msg.callback + ": " + msg.what);
            }

            //把消息派發(fā)給msg的target屬性,然后用dispatchMessage方法去處理
            msg.target.dispatchMessage(msg);

            if (logging != null) {
                logging.println("<<<<< Finished to " + msg.target + " " + msg.callback);
            }

            // Make sure that during the course of dispatching the
            // identity of the thread wasn't corrupted.
            final long newIdent = Binder.clearCallingIdentity();
            if (ident != newIdent) {
                Log.wtf(TAG, "Thread identity changed from 0x"
                        + Long.toHexString(ident) + " to 0x"
                        + Long.toHexString(newIdent) + " while dispatching to "
                        + msg.target.getClass().getName() + " "
                        + msg.callback + " what=" + msg.what);
            }

            msg.recycleUnchecked();
        }
    }

綜上熬拒,可以看出Looper的作用是:
1.實(shí)例化Looper對(duì)象本身(prepare()方法)爷光,創(chuàng)建與之對(duì)應(yīng)的MessageQueue(looper()構(gòu)造方法)
2.loop()方法不斷從MessageQueue中取消息,派發(fā)給Handler澎粟,然后調(diào)用相應(yīng)Handler的dispatchMessage()方法進(jìn)行消息處理蛀序。

那么,Handler是如何和Looper綁定且從MessageQueue中獲取消息執(zhí)行的呢活烙?
來(lái)看Handler的構(gòu)造方法:

    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());
            }
        }
      
        //Looper.myLooper()獲得了當(dāng)前線程保存的Looper實(shí)例
        mLooper = Looper.myLooper();

        //如果沒有l(wèi)ooper實(shí)例就會(huì)拋出異常徐裸,這說(shuō)明一個(gè)沒有創(chuàng)建looper的線程中是無(wú)法創(chuàng)建一個(gè)Handler對(duì)象的;
        //子線程中創(chuàng)建一個(gè)Handler時(shí)需要?jiǎng)?chuàng)建Looper啸盏,且開啟循環(huán)才能使用這個(gè)Handler
        if (mLooper == null) {
            throw new RuntimeException(
                "Can't create handler inside thread that has not called Looper.prepare()");
        }

        //獲取looper實(shí)例的MessageQueue重贺,保證handler實(shí)例與looper實(shí)例的MessageQueue關(guān)聯(lián)
        mQueue = mLooper.mQueue;
        mCallback = callback;
        mAsynchronous = async;
    }

以上,當(dāng)Handler構(gòu)造函數(shù)初始化時(shí)回懦,自動(dòng)關(guān)聯(lián)looper和對(duì)應(yīng)的MessageQueue气笙。

Handler向MessageQueue發(fā)送消息的代碼sendMessage執(zhí)行后,是發(fā)生了什么怯晕?

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

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

    //向下調(diào)用
    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);
    }

    //sendMessage最后的最后調(diào)用到了enqueueMessage方法
    private boolean enqueueMessage(MessageQueue queue, Message msg, long uptimeMillis) {
        // msg.target = this潜圃,也就是把當(dāng)前handler作為msg的target屬性
        // 在Looper的loop()方法中會(huì)取出msg,然后執(zhí)行msg.target.dispatchMessage(msg)去處理消息
        msg.target = this;
        if (mAsynchronous) {
            msg.setAsynchronous(true);
        }

        //handler發(fā)出的消息最終會(huì)保存到消息隊(duì)列中
        return queue.enqueueMessage(msg, uptimeMillis);
    }

Handler的post()和sendMessage()有什么不同舟茶?

    public final boolean post(Runnable r) {
       return  sendMessageDelayed(getPostMessage(r), 0);
    }
    
    private static Message getPostMessage(Runnable r) {
        //使用obtain方法創(chuàng)建一個(gè)Message對(duì)象谭期,因?yàn)镸essage內(nèi)部維護(hù)了一個(gè)Message池用于Message復(fù)用,避免使用new重新分配內(nèi)存
        Message m = Message.obtain();
        //將創(chuàng)建的Runnable對(duì)象作為callback屬性吧凉,賦值給message
        m.callback = r;
        //返回一個(gè)message對(duì)象
        return m;
    }

繼續(xù)向下調(diào)用:

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

    //向下調(diào)用
    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);
    }

由上可知隧出,Handler的post()方法和sendmessage()方法一樣,最終都調(diào)用了sendMessageAtTime阀捅,然后調(diào)用了enqueueMessage方法胀瞪,將msg.target賦值為handler,然后將Handler加入到MessageQueue中饲鄙。

But赏廓,在使用post方法時(shí)涵紊,將創(chuàng)建的Runnable對(duì)象作為callback屬性賦值給了message,那么該如何執(zhí)行handler的回調(diào)方法呢幔摸?請(qǐng)看如下代碼:

    /**
     * Handle system messages here.
     */
    public void dispatchMessage(Message msg) {
        //如果msg.callback不為null,則執(zhí)行handleCallback回調(diào)颤练,也就是我們的Runnable里的回調(diào)
        if (msg.callback != null) {
            handleCallback(msg);
        } else {
            if (mCallback != null) {
                if (mCallback.handleMessage(msg)) {
                    return;
                }
            }
            handleMessage(msg);
        }
    }

    //Runnable的run()方法中執(zhí)行回調(diào)函數(shù)
    private static void handleCallback(Message message) {
        message.callback.run();
    }

    /**
     * Subclasses must implement this to receive messages.
     */
    public void handleMessage(Message msg) {
    }

可以看到dispatchMessage()方法中調(diào)用了handleMessage()方法既忆,但是是一個(gè)空方法,在創(chuàng)建Handler時(shí)通過(guò)復(fù)寫handleMessage()方法來(lái)實(shí)現(xiàn)我們需要的消息處理方式嗦玖,根據(jù)msg.what標(biāo)識(shí)進(jìn)行區(qū)分處理患雇。

為什么在UI線程中使用Handler時(shí)不需要?jiǎng)?chuàng)建Looper?

當(dāng)一個(gè)Android應(yīng)用程序啟動(dòng)時(shí)宇挫,會(huì)創(chuàng)建一個(gè)主線程ActivityThread苛吱,在ActivityThread中有一個(gè)靜態(tài)的main()方法,也是應(yīng)用程序的入口點(diǎn)器瘪。

 public static void main(String[] args) {
        ...
        //  通過(guò)prepareMainLooper方法為主線程創(chuàng)建一個(gè)looper
        Looper.prepareMainLooper();
        ActivityThread thread = new ActivityThread();
        thread.attach(false);

        if (sMainThreadHandler == null) {
            sMainThreadHandler = thread.getHandler();
        }
        ...
        // 開啟消息循環(huán)
        Looper.loop();
        ...
    }

    /**
     * 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()方法用于創(chuàng)建一個(gè)looper對(duì)象
        //主線程的消息循環(huán)是不允許退出的
        prepare(false);
        synchronized (Looper.class) {
            if (sMainLooper != null) {
                throw new IllegalStateException("The main Looper has already been prepared.");
            }
            sMainLooper = myLooper();
        }
    }

舉幾個(gè)例子來(lái)加強(qiáng)理解

子線程和主線程通信翠储,使用sendMessage()更新UI

/**
 * Created by Kathy on 17-2-15.
 * 子線程與主線程通信
 */

public class MainActivity extends Activity {

    private static final int INT_ONE = 1;

    //在主線程里創(chuàng)建一個(gè)mHandler實(shí)例
    private Handler mHandler = new Handler(new Handler.Callback() {
        @Override
        public boolean handleMessage(Message msg) {
            switch (msg.what) {
                case INT_ONE:
                    String text = (String) msg.obj;
                    Toast.makeText(MainActivity.this, text,Toast.LENGTH_SHORT).show();
                    break;
                default:
                    break;
            }
            return false;
        }
    });


    class ThreadOne extends Thread{
        @Override
        public void run() {
            //創(chuàng)建需要發(fā)送的消息,注意使用的是主線程的mHandler橡疼,所以也在主線程的handleMessage()中回調(diào)
            Message msg = Message.obtain();
            msg.what = INT_ONE; //標(biāo)識(shí)消息
            msg.obj = "One"; //存放消息
            mHandler.sendMessage(msg);
        }
    }


    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        //啟動(dòng)子線程
        new ThreadOne().start();
    }
}

啟動(dòng)這個(gè)Activity援所,執(zhí)行子線程,在子線程中通過(guò)UI線程的mHandler發(fā)送消息欣除,會(huì)發(fā)現(xiàn)最終會(huì)執(zhí)行到handleMessage()方法住拭,彈出Toast。

使用Handler.post()更新UI

/**
 * Created by Kathy on 17-2-16.
 * 使用Handler.post()通信
 */

public class MainActivityTwo extends Activity {

    private Handler mHandler;

    //使用mHandler.post()方法只需要在run()方法中寫回調(diào)內(nèi)容即可
    class ThreadTwo extends Thread {
        @Override
        public void run() {
            mHandler.post(new Runnable() {
                @Override
                public void run() {
                    Toast.makeText(MainActivityTwo.this, "Two", Toast.LENGTH_SHORT).show();
                }
            });
        }
    }

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);

        // 實(shí)例化Handler历帚,無(wú)需指定looper滔岳,mHandler自動(dòng)綁定當(dāng)前線程(UI線程)的Looper和MessageQueue
        mHandler = new Handler();

        new ThreadTwo().start();
    }
}

主線程如何向子線程中發(fā)送消息的呢?

/**
 * Created by Kathy on 17-2-16.
 */

public class MainActivityThree extends Activity {

    private Handler mChildHandlerOne;
    private Handler mChildHandlerTwo;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);

        Log.d("Kathy", "mainLooper = " + getMainLooper());

        new Thread(new Runnable() {
            @Override
            public void run() {
                Looper.prepare();
                //子線程中實(shí)例化mChildHandlerOne
                mChildHandlerOne = new Handler(new Handler.Callback() {
                    @Override
                    public boolean handleMessage(Message msg) {
                        Log.d("Kathy", "mChildHandlerOne received Message!");

                        // 在此線程中,用mChildHandlerTwo發(fā)送消息
                        // 消息被加入到mChildHandlerTwo的消息隊(duì)列中挽牢,進(jìn)而會(huì)執(zhí)行到mChildHandlerTwo的回調(diào)方法
                        // 這就完成了子線程向子線程之前發(fā)消息的可能
                        Message msg2 = Message.obtain();
                        msg2.what = 1;
                        mChildHandlerTwo.sendMessage(msg2);
                        return false;
                    }
                });
                Log.d("Kathy", "mChildHandlerOne = " + mChildHandlerOne.getLooper());
                Looper.loop();

            }
        }).start();

        new Thread(new Runnable() {
            @Override
            public void run() {
                Looper.prepare();
                //子線程中實(shí)例化mChildHandlerTwo
                mChildHandlerTwo = new Handler(new Handler.Callback() {
                    @Override
                    public boolean handleMessage(Message msg) {
                        Log.d("Kathy", "mChildHandlerTwo received Message!");
                        return false;
                    }
                });
                Log.d("Kathy", "mChildHandlerTwo = " + mChildHandlerTwo.getLooper());
                Looper.loop();
            }
        }).start();

        // 點(diǎn)擊按鈕谱煤,如果兩個(gè)子線程中的handler都被實(shí)例化后,在主線程中卓研,用mChildHandlerOne發(fā)送消息
        // 消息會(huì)加入到mChildHandlerOne的消息隊(duì)列中趴俘,進(jìn)而會(huì)執(zhí)行到mChildHandlerOne的回調(diào)方法
        // 這就完成了在主線程中向子線程發(fā)送消息的可能
        findViewById(R.id.btn).setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                if (null != mChildHandlerOne && null != mChildHandlerTwo) {
                    Message msg1 = Message.obtain();
                    msg1.what = 1;
                    mChildHandlerOne.sendMessage(msg1);
                }
            }
        });
    }
}

Log輸出如下:

02-17 15:41:14.998 22131-22131/com.demo.kathy.demo D/Kathy: mainLooper = Looper (main, tid 1) {56801a8}
02-17 15:41:15.007 22131-22170/com.demo.kathy.demo D/Kathy: mChildHandlerOne = Looper (Thread-5565, tid 5565) {dba14c1}
02-17 15:41:15.009 22131-22171/com.demo.kathy.demo D/Kathy: mChildHandlerTwo = Looper (Thread-5566, tid 5566) {8d3d466}
02-17 15:41:28.490 22131-22170/com.demo.kathy.demo D/Kathy: mChildHandlerOne received Message!
02-17 15:41:28.490 22131-22171/com.demo.kathy.demo D/Kathy: mChildHandlerTwo received Message!

若有錯(cuò)誤,請(qǐng)及時(shí)指出奏赘,感謝閱讀!

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末寥闪,一起剝皮案震驚了整個(gè)濱河市,隨后出現(xiàn)的幾起案子磨淌,更是在濱河造成了極大的恐慌疲憋,老刑警劉巖,帶你破解...
    沈念sama閱讀 206,968評(píng)論 6 482
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件梁只,死亡現(xiàn)場(chǎng)離奇詭異缚柳,居然都是意外死亡埃脏,警方通過(guò)查閱死者的電腦和手機(jī),發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 88,601評(píng)論 2 382
  • 文/潘曉璐 我一進(jìn)店門秋忙,熙熙樓的掌柜王于貴愁眉苦臉地迎上來(lái)彩掐,“玉大人,你說(shuō)我怎么就攤上這事灰追《掠模” “怎么了?”我有些...
    開封第一講書人閱讀 153,220評(píng)論 0 344
  • 文/不壞的土叔 我叫張陵弹澎,是天一觀的道長(zhǎng)朴下。 經(jīng)常有香客問(wèn)我,道長(zhǎng)苦蒿,這世上最難降的妖魔是什么殴胧? 我笑而不...
    開封第一講書人閱讀 55,416評(píng)論 1 279
  • 正文 為了忘掉前任,我火速辦了婚禮佩迟,結(jié)果婚禮上团滥,老公的妹妹穿的比我還像新娘。我一直安慰自己音五,他們只是感情好惫撰,可當(dāng)我...
    茶點(diǎn)故事閱讀 64,425評(píng)論 5 374
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著躺涝,像睡著了一般厨钻。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上坚嗜,一...
    開封第一講書人閱讀 49,144評(píng)論 1 285
  • 那天夯膀,我揣著相機(jī)與錄音,去河邊找鬼苍蔬。 笑死诱建,一個(gè)胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的碟绑。 我是一名探鬼主播俺猿,決...
    沈念sama閱讀 38,432評(píng)論 3 401
  • 文/蒼蘭香墨 我猛地睜開眼,長(zhǎng)吁一口氣:“原來(lái)是場(chǎng)噩夢(mèng)啊……” “哼格仲!你這毒婦竟也來(lái)了押袍?” 一聲冷哼從身側(cè)響起,我...
    開封第一講書人閱讀 37,088評(píng)論 0 261
  • 序言:老撾萬(wàn)榮一對(duì)情侶失蹤凯肋,失蹤者是張志新(化名)和其女友劉穎谊惭,沒想到半個(gè)月后,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 43,586評(píng)論 1 300
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡圈盔,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 36,028評(píng)論 2 325
  • 正文 我和宋清朗相戀三年豹芯,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片驱敲。...
    茶點(diǎn)故事閱讀 38,137評(píng)論 1 334
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡铁蹈,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出癌佩,到底是詐尸還是另有隱情木缝,我是刑警寧澤,帶...
    沈念sama閱讀 33,783評(píng)論 4 324
  • 正文 年R本政府宣布围辙,位于F島的核電站,受9級(jí)特大地震影響放案,放射性物質(zhì)發(fā)生泄漏姚建。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 39,343評(píng)論 3 307
  • 文/蒙蒙 一吱殉、第九天 我趴在偏房一處隱蔽的房頂上張望掸冤。 院中可真熱鬧,春花似錦友雳、人聲如沸稿湿。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,333評(píng)論 0 19
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)饺藤。三九已至,卻和暖如春流礁,著一層夾襖步出監(jiān)牢的瞬間涕俗,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 31,559評(píng)論 1 262
  • 我被黑心中介騙來(lái)泰國(guó)打工神帅, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留再姑,地道東北人。 一個(gè)月前我還...
    沈念sama閱讀 45,595評(píng)論 2 355
  • 正文 我出身青樓找御,卻偏偏與公主長(zhǎng)得像元镀,于是被迫代替她去往敵國(guó)和親。 傳聞我的和親對(duì)象是個(gè)殘疾皇子霎桅,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 42,901評(píng)論 2 345

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