Android消息機(jī)制之Handler、MessageQueue趣钱、Looper源碼分析

1涌献、什么是Android消息機(jī)制?什么是Handler?為什么要用Handler首有?

Android消息機(jī)制主要是指Handler的運(yùn)行機(jī)制燕垃,Handler運(yùn)行需要底層的MessageQueue和Looper支撐。其中MessageQueue采用的是單鏈表的數(shù)據(jù)結(jié)構(gòu)來(lái)存儲(chǔ)消息列表绞灼,Looper可以理解為消息循環(huán)利术。由于MessageQueue只是一個(gè)消息存儲(chǔ)單元呈野,不能去處理消息低矮,而Looper就是用來(lái)處理消息的,Looper會(huì)以無(wú)限循環(huán)的形式去MessageQueue中查找是否有新消息被冒,如果有則取出消息并處理军掂,否則就一直阻塞等待。Looper中還有一個(gè)特殊的概念是ThreadLocal昨悼,ThreadLocal的作用是可以在每個(gè)線程中存儲(chǔ)數(shù)據(jù)蝗锥,在Handler創(chuàng)建的時(shí)候會(huì)采用當(dāng)前線程的Looper來(lái)構(gòu)造消息循環(huán)系統(tǒng),Handler內(nèi)部就是通過ThreadLocal來(lái)獲取每個(gè)線程的Looper率触,線程默認(rèn)是沒有Looper的终议,在使用Handler之前必須先為主線程創(chuàng)建Looper。大家可能有疑惑了,我們?cè)诖a中使用Handler的時(shí)候穴张,并沒有為主線程去創(chuàng)建Looper的代碼细燎,為什么也可以正常使用呢?那是因?yàn)橹骶€程中ActivityThread被創(chuàng)建時(shí)已經(jīng)初始化主線程的Looper皂甘。

Handler主要用于異步消息的處理:當(dāng)發(fā)出一個(gè)消息(Message)之后玻驻,首先進(jìn)入一個(gè)消息隊(duì)列(MessageQueue),發(fā)送消息的函數(shù)(sendMessage())即刻返回偿枕,而另外一個(gè)部分在消息隊(duì)列中逐一將消息取出璧瞬,然后對(duì)消息進(jìn)行處理,也就是發(fā)送消息和接收消息不是同步的處理渐夸。 這種機(jī)制通常用來(lái)處理相對(duì)耗時(shí)比較長(zhǎng)的操作嗤锉,比如從服務(wù)端拉取一些數(shù)據(jù)、下載圖片等墓塌。

在Android中規(guī)定訪問UI只能在主線程中進(jìn)行(因?yàn)锳ndroid的UI控件不是線程安全的档冬,如果在多線程中并發(fā)訪問可能會(huì)導(dǎo)致UI控件處于不可預(yù)期的狀態(tài)),如果在子線程訪問UI桃纯,程序會(huì)拋出異常酷誓。如在子線程中拉取服務(wù)端數(shù)據(jù)之后需要更新UI上控件展示內(nèi)容,這個(gè)時(shí)候就需要切換到主線程去操作UI态坦,此時(shí)就可以通過Handler將操作UI的工作切換到主線程去完成涂乌。因此,系統(tǒng)之所以提供Handler莱坎,主要原因就是為了解決在子線程中無(wú)法訪問UI的矛盾倔撞。

2、Handler簡(jiǎn)單使用

2.1谜诫、創(chuàng)建Handler實(shí)例的兩種方式

2.1.1 通過Handler.Callback

    private Handler handler = new Handler(new Handler.Callback() {
        @Override
        public boolean handleMessage(Message msg) {
            return false;
        }
    });

2.1.2 通過派生Handler的子類

    private Handler handler = new Handler() {
        @Override
        public void handleMessage(Message msg) {
            super.handleMessage(msg);
        }
    };

2.2漾峡、Handler發(fā)送消息常用的兩種方式

2.2.1 sendMessage

Message message = new Message();
message.obj = "JokerWan";
message.what = 666;
// 發(fā)送消息
handler.sendMessage(message);
// 發(fā)送延遲消息
handler.sendMessageDelayed(message,3000);

2.2.2 post

handler.post(new Runnable() {
    @Override
    public void run() {
        // action           
    }
});     

3、Handler運(yùn)行機(jī)制原理圖

Android消息機(jī)制原理圖.png

4喻旷、Handler運(yùn)行機(jī)制源碼分析

溫馨提示:為了方便大家閱讀生逸,我這里貼出的代碼會(huì)對(duì)源碼進(jìn)行刪減,只保留部分跟本文相關(guān)的關(guān)鍵代碼且预,其他代碼用...代替

4.1槽袄、主線程Looper的創(chuàng)建

首先我們看到ActivityThread類中的main()方法的代碼

    public static void main(String[] args) {
        Trace.traceBegin(Trace.TRACE_TAG_ACTIVITY_MANAGER, "ActivityThreadMain");

        ...

        Looper.prepareMainLooper();

        ...

        if (sMainThreadHandler == null) {
            sMainThreadHandler = thread.getHandler();
        }

        ...
        
        Looper.loop();

        throw new RuntimeException("Main thread loop unexpectedly exited");
    }

其中有兩個(gè)關(guān)鍵代碼Looper.prepareMainLooper();Looper.loop();,我們先看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();
        }
    }

繼續(xù)跟進(jìn)其中調(diào)用的prepare(false);方法

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

這個(gè)方法中用到了sThreadLocal變量锋谐,找到它的聲明

static final ThreadLocal<Looper> sThreadLocal = new ThreadLocal<Looper>();

類型是ThreadLocal遍尺,泛型是Looper,ThreadLocal是一個(gè)線程內(nèi)部的數(shù)據(jù)存儲(chǔ)類涮拗,通過它可以在指定的線程存儲(chǔ)數(shù)據(jù)乾戏,其他線程無(wú)法獲取該線程數(shù)據(jù)迂苛。我們繼續(xù)跟進(jìn)下sThreadLocal.get()方法,ThreadLocal#get()

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

調(diào)用getMap方法并傳入當(dāng)前線程t鼓择,返回保存當(dāng)前線程中的數(shù)據(jù)ThreadLocal.ThreadLocalMap的對(duì)象map灾部,ThreadLocalMap類似于HashMap,只不過ThreadLocalMap僅僅是用來(lái)存儲(chǔ)線程的ThreadLocal數(shù)據(jù)惯退,首先通過getMap()獲取當(dāng)前線程的ThreadLocal數(shù)據(jù)的map赌髓,接著用ThreadLocal的對(duì)象作為key,取出當(dāng)前線程的ThreadLocal數(shù)據(jù)的map中存儲(chǔ)的result催跪,由于前面sThreadLocal變量聲明的時(shí)候約定的泛型是Looper锁蠕,所以這里返回result對(duì)象就是Looper對(duì)象,繼續(xù)回到prepare()方法,首先取出當(dāng)前線程的Looper對(duì)象懊蒸,校驗(yàn)下Looper對(duì)象的唯一性荣倾,然后new Looper(quitAllowed)并保存在ThreadLocal當(dāng)前線程中的數(shù)據(jù)中。

接著跟進(jìn)Looper的構(gòu)造方法

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

在Looper的構(gòu)造函數(shù)中創(chuàng)建了一個(gè)MessageQueue的對(duì)象骑丸,并保存在Looper的一個(gè)全局變量mQueue中舌仍,所以,每個(gè)Looper對(duì)象都綁定了一個(gè)MessageQueue對(duì)象通危。

4.2铸豁、Handler發(fā)送消息到MessageQueue

4.2.1 Handler構(gòu)造器

    public Handler() {
        this(null, false);
    }
    public Handler(Callback callback, boolean async) {
        ...

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

首先調(diào)用Looper.myLooper();方法獲取Looper對(duì)象,并保存在mLooper變量中菊碟,看一下Looper#myLooper()

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

若獲取的Looper對(duì)象為null节芥,則拋出異常Can't create handler inside thread xxThread that has not called Looper.prepare(),因?yàn)樯厦嫖覀円呀?jīng)分析過了逆害,Looper對(duì)象的創(chuàng)建是在prepare()方法中头镊。
從ThreadLocal中獲取當(dāng)前線程(主線程)的Looper對(duì)象,接著將Looper中的MessageQueue保存到mQueue中魄幕,并將callback賦值給mCallback相艇,當(dāng)我們通過匿名內(nèi)部類創(chuàng)建Handler時(shí)callback為null。

4.2.2 Handler#sendMessage

    public final boolean sendMessage(Message msg) {
        return sendMessageDelayed(msg, 0);
    }
    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);
    }
    private boolean enqueueMessage(MessageQueue queue, Message msg, long uptimeMillis) {
        msg.target = this;
        if (mAsynchronous) {
            msg.setAsynchronous(true);
        }
        return queue.enqueueMessage(msg, uptimeMillis);
    }

可以看到不管是調(diào)用sendMessage還是sendMessageDelayed纯陨,還有后面會(huì)講到的post(runnable)坛芽,最終都會(huì)調(diào)用enqueueMessage方法,方法里面首先將Handler對(duì)象賦值給msg.target队丝,后面會(huì)通過msg.target獲取Handler對(duì)象去處理消息(后面會(huì)講到)靡馁,然后調(diào)用了mQueueenqueueMessage(msg, uptimeMillis)方法欲鹏,
跟進(jìn)MessageQueue#enqueueMessage()

    boolean enqueueMessage(Message msg, long when) {
        ...
        synchronized (this) {
            ...
            msg.markInUse();
            msg.when = when;
            Message p = mMessages;
            boolean needWake;
            if (p == null || when == 0 || when < p.when) {
                msg.next = p;
                mMessages = msg;
                needWake = mBlocked;
            } else {
                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;
            }
            ...
        }
        return true;
    }

MessageQueue是一個(gè)消息隊(duì)列机久,主要包含兩個(gè)操作,插入和讀取赔嚎,插入就是上面這個(gè)方法enqueueMessage膘盖,讀取是next方法胧弛,后面會(huì)講到。盡管MessageQueue叫消息隊(duì)列侠畔,但它內(nèi)部實(shí)現(xiàn)并不是隊(duì)列结缚,而是用單鏈表的數(shù)據(jù)結(jié)構(gòu)來(lái)維護(hù)消息列表,主要原因是單鏈表在插入和刪除上效率比較高软棺。從enqueueMessage方法的實(shí)現(xiàn)來(lái)看红竭,它的主要操作就是單鏈表的插入操作,將傳進(jìn)來(lái)的msg插入到鏈表中喘落,并將msg.next指向上一個(gè)傳進(jìn)來(lái)的Message茵宪。

4.2.3 Handler#post

查看post方法源碼

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

發(fā)現(xiàn)原來(lái)post方法也是調(diào)用的sendMessageDelayed方法去send Message,只是把我們傳進(jìn)來(lái)的Runnable對(duì)象通過調(diào)用getPostMessage方法構(gòu)造成一個(gè)Message

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

通過Message.obtain()獲取一個(gè)Message瘦棋,將Runnable對(duì)象賦值給m.callback稀火,并返回Message,到這里我們就知道了赌朋,原來(lái)post(Runnable r)方法只是把r放進(jìn)一個(gè)message中凰狞,并將message發(fā)送出去

4.3、Looper輪詢MessageQueue并將消息發(fā)送給Handler處理

前面我們已經(jīng)分析過沛慢,在ActivityThread類中的main()方法中會(huì)調(diào)用Looper.prepareMainLooper();Looper.loop();赡若,Looper.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;

        Binder.clearCallingIdentity();
        final long ident = Binder.clearCallingIdentity();

        ...

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

            ...

            try {
                msg.target.dispatchMessage(msg);
                ...
            } finally {
                ...
            }
            ...
            msg.recycleUnchecked();
        }
    }

首先獲取到Looper對(duì)象和Looper對(duì)象里面的MessageQueue,然后構(gòu)造一個(gè)死循環(huán)团甲,在循環(huán)里面通過調(diào)用MessageQueue的next()方法取出Message分發(fā)給Handler去處理消息斩熊,直到取出來(lái)的msg為null,則跳出循環(huán)伐庭。msg.targetenqueueMessage方法中賦值為發(fā)送此Message的Handler對(duì)象粉渠,這里取出Handler對(duì)象并調(diào)用其dispatchMessage(msg)去處理消息,而Handler的dispatchMessage(msg)是在創(chuàng)建Handler時(shí)所使用的Looper中執(zhí)行的圾另,這樣就成功的將代碼邏輯切換到了指定的線程去執(zhí)行霸株。來(lái)看一下MessageQueue的next()具體實(shí)現(xiàn)

Message next() {
        ...
        for (;;) {
            ...
            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;
                }

                ...
        }
    }

可以看到next是一個(gè)阻塞操作,開啟一個(gè)死循環(huán)來(lái)取Message集乔,當(dāng)沒有Message時(shí)去件,next方法就會(huì)一直阻塞在那里,這也導(dǎo)致Looper.loop()方法也一直阻塞扰路。

4.4尤溜、Handler處理消息

Looper對(duì)象從MessageQueue中取到Message然后調(diào)用Handler的dispatchMessage(msg)去處理消息

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

首先檢查msg的callback是否為null,不為null則調(diào)用handleCallback(msg)來(lái)處理消息汗唱,還記得上面我們分析的post(runnable)嗎宫莱,這里從msg取出的callback就是調(diào)用Handler的post方法傳入的Runnable對(duì)象,看下handleCallback(msg)代碼實(shí)現(xiàn)

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

實(shí)現(xiàn)很簡(jiǎn)單哩罪,就是取出Runnable對(duì)象并調(diào)用其中的run方法授霸。

dispatchMessage方法接著檢查mCallback是否為null巡验,不為null就調(diào)用其handleMessage(msg)方法,mCallback類型為Callback接口碘耳,上面我們講過Handler創(chuàng)建的兩種方式显设,第一種方式就是在Handler的構(gòu)造器中通過匿名內(nèi)部類傳入一個(gè)實(shí)現(xiàn)了Handler.Callback接口的對(duì)象,并在構(gòu)造器賦值給mCallback辛辨。

dispatchMessage方法最后調(diào)用Handler的handleMessage(msg)方法來(lái)處理消息捕捂,就是上面我們講到的Handler創(chuàng)建的第二種方式:派生一個(gè)Handler的子類并重寫其handleMessage方法來(lái)處理消息。

5斗搞、主線程的消息循環(huán)

前面我們分析了ActivityThread類中的main()會(huì)創(chuàng)建Looper和MessageQueue绞蹦,并將MessageQueue關(guān)聯(lián)到Looper中,調(diào)用Looper的loop方法來(lái)開啟主線程的消息循環(huán)榜旦,那么主線程的消息是怎么發(fā)送的呢幽七?ActivityThread類內(nèi)部定義了一個(gè)Handler的子類HActivityThread就是通過這個(gè)內(nèi)部類H來(lái)和消息隊(duì)列進(jìn)行交互并處理消息溅呢。

ActivityThread通過 ApplicationThread 和 AMS 進(jìn)行進(jìn)程間通信澡屡, AMS以進(jìn)程間通信的方式完成 ActivityThread 的請(qǐng)求后會(huì)回調(diào) ApplicationThread 中的 Binder 方法,然后 ApplicationThread會(huì)向 H 發(fā)送消息咐旧,H 收到消息后會(huì)將 ApplicationThread中的邏輯切換到 ActivityThread 中去執(zhí)行驶鹉,即切換到主線程中取執(zhí)行,這個(gè)過程就是主線程的消息循環(huán)模型铣墨。

6室埋、總結(jié)

  1. Handler 的背后有 Looper、MessageQueue 支撐伊约,Looper 負(fù)責(zé)消息分發(fā)姚淆, MessageQueue 負(fù)責(zé)消息管理;
  2. 在創(chuàng)建 Handler 之前一定需要先創(chuàng)建 Looper;
  3. Looper 有退出的功能,但是主線程的 Looper 不允許退出;
  4. 異步線程的 Looper 需要自己調(diào)用 Looper.myLooper().quit(); 退出;
  5. Runnable 被封裝進(jìn)了 Message屡律,可以說是一個(gè)特殊的 Message;
  6. Handler.handleMessage() 所在的線程是 Looper.loop() 方法被調(diào)用的線程腌逢,也可
    以說成 Looper 所在的線程,并不是創(chuàng)建 Handler 的線程;
  7. 使用內(nèi)部類的方式使用 Handler 可能會(huì)導(dǎo)致內(nèi)存泄露超埋,即便在 Activity.onDestroy
    里移除延時(shí)消息搏讶,必須要寫成靜態(tài)內(nèi)部類;
最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個(gè)濱河市霍殴,隨后出現(xiàn)的幾起案子媒惕,更是在濱河造成了極大的恐慌,老刑警劉巖来庭,帶你破解...
    沈念sama閱讀 218,451評(píng)論 6 506
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件妒蔚,死亡現(xiàn)場(chǎng)離奇詭異,居然都是意外死亡,警方通過查閱死者的電腦和手機(jī)面睛,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 93,172評(píng)論 3 394
  • 文/潘曉璐 我一進(jìn)店門絮蒿,熙熙樓的掌柜王于貴愁眉苦臉地迎上來(lái)尊搬,“玉大人叁鉴,你說我怎么就攤上這事》鹗伲” “怎么了幌墓?”我有些...
    開封第一講書人閱讀 164,782評(píng)論 0 354
  • 文/不壞的土叔 我叫張陵,是天一觀的道長(zhǎng)冀泻。 經(jīng)常有香客問我常侣,道長(zhǎng),這世上最難降的妖魔是什么弹渔? 我笑而不...
    開封第一講書人閱讀 58,709評(píng)論 1 294
  • 正文 為了忘掉前任胳施,我火速辦了婚禮,結(jié)果婚禮上肢专,老公的妹妹穿的比我還像新娘舞肆。我一直安慰自己,他們只是感情好博杖,可當(dāng)我...
    茶點(diǎn)故事閱讀 67,733評(píng)論 6 392
  • 文/花漫 我一把揭開白布椿胯。 她就那樣靜靜地躺著,像睡著了一般剃根。 火紅的嫁衣襯著肌膚如雪哩盲。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 51,578評(píng)論 1 305
  • 那天狈醉,我揣著相機(jī)與錄音廉油,去河邊找鬼。 笑死苗傅,一個(gè)胖子當(dāng)著我的面吹牛娱两,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播金吗,決...
    沈念sama閱讀 40,320評(píng)論 3 418
  • 文/蒼蘭香墨 我猛地睜開眼十兢,長(zhǎng)吁一口氣:“原來(lái)是場(chǎng)噩夢(mèng)啊……” “哼!你這毒婦竟也來(lái)了摇庙?” 一聲冷哼從身側(cè)響起旱物,我...
    開封第一講書人閱讀 39,241評(píng)論 0 276
  • 序言:老撾萬(wàn)榮一對(duì)情侶失蹤,失蹤者是張志新(化名)和其女友劉穎卫袒,沒想到半個(gè)月后宵呛,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 45,686評(píng)論 1 314
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡夕凝,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 37,878評(píng)論 3 336
  • 正文 我和宋清朗相戀三年宝穗,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了户秤。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點(diǎn)故事閱讀 39,992評(píng)論 1 348
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡逮矛,死狀恐怖鸡号,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情须鼎,我是刑警寧澤鲸伴,帶...
    沈念sama閱讀 35,715評(píng)論 5 346
  • 正文 年R本政府宣布,位于F島的核電站晋控,受9級(jí)特大地震影響汞窗,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜赡译,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,336評(píng)論 3 330
  • 文/蒙蒙 一仲吏、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧蝌焚,春花似錦裹唆、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,912評(píng)論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)。三九已至红碑,卻和暖如春舞吭,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背析珊。 一陣腳步聲響...
    開封第一講書人閱讀 33,040評(píng)論 1 270
  • 我被黑心中介騙來(lái)泰國(guó)打工羡鸥, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留,地道東北人忠寻。 一個(gè)月前我還...
    沈念sama閱讀 48,173評(píng)論 3 370
  • 正文 我出身青樓惧浴,卻偏偏與公主長(zhǎng)得像,于是被迫代替她去往敵國(guó)和親奕剃。 傳聞我的和親對(duì)象是個(gè)殘疾皇子衷旅,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 44,947評(píng)論 2 355

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