你真的懂Handler嗎?Handler問(wèn)答

2018年8月1日以前謝絕全文轉(zhuǎn)載(已授權(quán)除外)
本文作者:@怪盜kidou
本文鏈接:http://www.lxweimin.com/p/f70ee1765a61

周末在家有點(diǎn)兒無(wú)聊,不知道該干些啥,想了想開(kāi)通博客這么長(zhǎng)時(shí)間以來(lái)好像并沒(méi)有些什么關(guān)于 Android 的東西,所以這次來(lái)寫(xiě)寫(xiě)Android 相關(guān)的博客 —— Handler。

為什么寫(xiě) Handler

確實(shí) HandlerAndroid 開(kāi)發(fā)過(guò)程中非常非常常見(jiàn)的東西,講Handler的博客也不勝枚舉為什么我還要寫(xiě)關(guān)于Handler的內(nèi)容?

起因是這樣的,公司為了擴(kuò)張業(yè)務(wù)準(zhǔn)備做一個(gè)新的產(chǎn)品線所以給移動(dòng)端這邊分配了4個(gè)招聘名額(iOS和Android各兩名),頭一個(gè)星期我因?yàn)樵诿χ鲂枨蟛](méi)有參與公司的面試,都是公司的另外兩個(gè)同事在參與面試,后一個(gè)星期我也參與到其中,但是我發(fā)現(xiàn)一個(gè)很?chē)?yán)重的問(wèn)題:在我面試的幾個(gè)人雖然工作經(jīng)驗(yàn)都集中3~6年但都沒(méi)有把 Handler 講清楚。

與其他的博客有什么不同

市面上有太多講 Handler 的博客了,那我的博客要如何做到讓人耳目一新并且切實(shí)能讓大家受益呢?

我想了一下,Handler的基本原理很簡(jiǎn)單,但細(xì)節(jié)還是蠻多的,這次發(fā)現(xiàn)問(wèn)題也是通過(guò)面試得出的,所以我決定通過(guò)模擬面試的方式告訴你關(guān)于 Handler 的那些事兒。

約定

本文的各個(gè)問(wèn)題只是我自己想出來(lái)的,并不是出自真實(shí)的面試中(除了部分我面試別人時(shí)的提問(wèn)),其他的均為我為了給大家介紹 Handler 機(jī)制時(shí)想出的問(wèn)題。

本文后面會(huì)出現(xiàn)的部分源碼,為避免小伙伴們?cè)?Android Studio 中看到代碼與我博客中的不一致,這里先統(tǒng)一一下環(huán)境:

  • sdk版本:API 27
android{
  compileSdkVersion 27
  // ......
}
  • 源碼版本:27_r03
深度截圖_選擇區(qū)域_20180623165118.png

深度截圖_選擇區(qū)域_20180623165324.png

Q:說(shuō)一下 Handler機(jī)制中涉及到那些類(lèi),各自的功能是什么

A:HandlerLooperMessageQueueMessage,主要是這4個(gè),ThreadLocal 可以不算在里面畢竟這個(gè)是JDK本身自帶類(lèi)不是專(zhuān)門(mén)為Handler機(jī)制設(shè)計(jì)的。

Handler 的作用是將 Message 對(duì)象發(fā)送到 MessageQueue 中去,同時(shí)將自己的引用賦值給 Message#target

Looper 的作用是將 Message 對(duì)象從 MessageQueue 中取出來(lái),并將其交給 Handler#dispatchMessage(Message) 方法,這里需要主要的是:不是調(diào)用 Handler#handleMessage(Message) 方法,具體原因后面會(huì)講。

MessageQueue 的作用負(fù)責(zé)插入和取出 Message

Q:Handler 有哪些發(fā)送消息的方法

我主要是看其知不知道 post 相關(guān)的方法,問(wèn)了兩個(gè)人兩人都不知道有post方法

sendMessage(Message msg)
sendMessageDelayed(Message msg, long uptimeMillis)
post(Runnable r)
postDelayed(Runnable r, long uptimeMillis)
sendMessageAtTime(Message msg,long when)

下面的幾個(gè)方法在我眼中可能并不是那么重要

sendEmptyMessage(int what)
sendEmptyMessageDelayed(int what, long uptimeMillis)
sendEmptyMessageAtTime(int what, long when)

Q:MessageQueue 中的 Message 是有序的嗎?排序的依據(jù)是什么

是有序的。你可能會(huì)想這不是廢話嘛,Queue 都是有序的,Set 才是無(wú)序的,這里想問(wèn)你的是他的內(nèi)部是基于什么進(jìn)行的排序,排序的依據(jù)是 Message#when 字段,表示一個(gè)相對(duì)時(shí)間,該值是由 MessageQueue#enqueueMessage(Message, Long) 方法設(shè)置的。

// 見(jiàn) MessageQueue.java:554,566~578
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;
                    // 一致循環(huán),直到找到尾巴(p == null)
                    // 或者這個(gè) message 的 when 小于我們當(dāng)前這個(gè) message 的 when
                    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;
}

如果當(dāng)前插入的 message#when 是介于 5~8 之間,那么for 循環(huán)結(jié)束時(shí) prevp 指向的樣子應(yīng)該是下圖的

prev和p的關(guān)系

由于這個(gè)特性,所以當(dāng)兩個(gè) Message#when 一致時(shí)插入序按先后順序,比如兩個(gè)的 when 都是7,那么第一個(gè)進(jìn)入后的樣子如下圖(黃):

第一個(gè) 7 入隊(duì)列后

第二個(gè)進(jìn)入后的樣子(紅):

第二個(gè) 7 入隊(duì)列后

Q:Message#when 是指的是什么

Message#when 是一個(gè)時(shí)間,用于表示 Message 期望被分發(fā)的時(shí)間,該值是 SystemClock#uptimeMillis()delayMillis 之和。

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

SystemClock#uptimeMillis() 是一個(gè)表示當(dāng)前時(shí)間的一個(gè)相對(duì)時(shí)間,它代表的是 自系統(tǒng)啟動(dòng)開(kāi)始從0開(kāi)始的到調(diào)用該方法時(shí)相差的毫秒數(shù)

Q:Message#when 為什么不用 System.currentTimeMillis() 來(lái)表示

System.currentTimeMillis() 代表的是從 1970-01-01 00:00:00 到當(dāng)前時(shí)間的毫秒數(shù),這個(gè)值是一個(gè)強(qiáng)關(guān)聯(lián) 系統(tǒng)時(shí)間 的值,我們可以通過(guò)修改系統(tǒng)時(shí)間達(dá)到修改該值的目的,所以該值是不可靠的值。

比如手機(jī)長(zhǎng)時(shí)間沒(méi)有開(kāi)機(jī),開(kāi)機(jī)后系統(tǒng)時(shí)間重置為出廠時(shí)設(shè)置的時(shí)間,中間我們發(fā)送了一個(gè)延遲消息,過(guò)了一段時(shí)間通過(guò) NTP 同步了最新時(shí)間,那么就會(huì)導(dǎo)致 延遲消息失效

同時(shí) Message#when 只是用 時(shí)間差 來(lái)表示先后關(guān)系,所以只需要一個(gè)相對(duì)時(shí)間就可以達(dá)成目的,它可以是從系統(tǒng)啟動(dòng)開(kāi)始計(jì)時(shí)的,也可以是從APP啟動(dòng)時(shí)開(kāi)始計(jì)時(shí)的,甚至可以是定期重置的(所有消息都減去同一個(gè)值,不過(guò)這樣就復(fù)雜了沒(méi)有必要)。

Q:子線程中可以創(chuàng)建 Handler 對(duì)象嗎?

不可以在子線程中直接調(diào)用 Handler 的無(wú)參構(gòu)造方法,因?yàn)?Handler 在創(chuàng)建時(shí)必須要綁定一個(gè) Looper 對(duì)象,有兩種方法綁定

  • 先調(diào)用 Looper.prepare() 在當(dāng)前線程初始化一個(gè) Looper
Looper.prepare();
Handler handler = new Handler();
// ....
// 這一步可別可少了
Looper.loop();
  • 通過(guò)構(gòu)造方法傳入一個(gè) Looper
Looper looper = .....;
Handler handler = new Handler(looper);

Q:Handler 是如何與 Looper 關(guān)聯(lián)的

上個(gè)問(wèn)題應(yīng)該告知了其中一種情況:通過(guò)構(gòu)造方法傳參。

還有一種是我們直接調(diào)用無(wú)參構(gòu)造方法時(shí)會(huì)有一個(gè)自動(dòng)綁定過(guò)程

// Handler.java:192
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;
}

Q:Looper 是如何與 Thread 關(guān)聯(lián)的

Looper 與 Thread 之間是通過(guò) ThreadLocal 關(guān)聯(lián)的,這個(gè)可以看 Looper#prepare() 方法

// Looper.java:93
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 中有一個(gè) ThreadLocal 類(lèi)型的 sThreadLocal靜態(tài)字段,Looper通過(guò)它的 getset 方法來(lái)賦值和取值。

由于 ThreadLocal是與線程綁定的,所以我們只要把 LooperThreadLocal 綁定了,那 LooperThread 也就關(guān)聯(lián)上了

ThreadLocal的原理在問(wèn) Handler 機(jī)制的時(shí)候也是一個(gè)比較常問(wèn)的點(diǎn),但是介紹的博客很多,源碼也沒(méi)有多少,這里就不再介紹了,如果有需要的話后期會(huì)寫(xiě)新博客。

Q:Handler 有哪些構(gòu)造方法

如果你上面的問(wèn)題 子線程中可以創(chuàng)建 Handler 對(duì)象嗎 沒(méi)有答上的話,我一般會(huì)通過(guò)這個(gè)問(wèn)題引導(dǎo)一下。

問(wèn)這個(gè)問(wèn)題主要是想問(wèn)你構(gòu)造方法可以傳那些參數(shù),并不是要你完全說(shuō)出來(lái),但是當(dāng)你知道可以傳哪些參數(shù)的時(shí)候,也可以推算出來(lái)有幾個(gè)構(gòu)造方法。

先說(shuō)可以傳那些類(lèi)型(僅限開(kāi)放API,被 @hide 標(biāo)注的不算在內(nèi)),僅兩種類(lèi)型:LooperHandler$Callback,那么我們就可以退算出有多少個(gè)公共構(gòu)造方法了:無(wú)參、單Looper、單Callback、Looper和Handler,共4種。

public Handler() {
    this(null, false);
}
public Handler(Callback callback) {
    this(callback, false);
}
public Handler(Looper looper) {
    this(looper, null, false);
}
public Handler(Looper looper, Callback callback) {
    this(looper, callback, false);
}

還有一個(gè) boolean 的 async, 不過(guò)這個(gè)不是開(kāi)放API,即使不知道個(gè)人覺(jué)得完全沒(méi)有問(wèn)題。

Q:looper為什么調(diào)用的是Handler的dispatchMessage方法

看一下dispatchMessage方法

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

從上面的代碼不難看出有兩個(gè)原因:

  • 當(dāng)msg.callback != null時(shí)會(huì)執(zhí)行 handleCallback(msg),這表示這個(gè) msg 對(duì)象是通過(guò) handler#postAtTime(Runnable, long) 相關(guān)方法發(fā)送的,所以 msg.whatmsg.obj 都是零值,不會(huì)交給Handler#handleMessage方法。
  • 從上一個(gè)問(wèn)題你應(yīng)該看到了Handler可以接受一個(gè) Callback 參數(shù),類(lèi)似于 View 里的 OnTouchListener ,會(huì)先把事件交給 Callback#handleMessage(Message) 處理,如果返回 false 時(shí)該消息才會(huì)交給 Handler#handleMessage(Message)方法。

Q:在子線程中如何獲取當(dāng)前線程的 Looper

Looper.myLooper()

內(nèi)部原理就是同過(guò)上面提到的 sThreadLocal#get() 來(lái)獲取 Looper

// Looper.java:203
public static @Nullable Looper myLooper() {
    return sThreadLocal.get();
}

Q:如果在任意線程獲取主線程的 Looper

Looper.getMainLooper()

這個(gè)在我們開(kāi)發(fā) library 時(shí)特別有用,畢竟你不知道別人在調(diào)用使用你的庫(kù)時(shí)會(huì)在哪個(gè)線程初始化,所以我們?cè)趧?chuàng)建 Handler 時(shí)每次都通過(guò)指定主線程的 Looper 的方式保證庫(kù)的正常運(yùn)行。

Q:如何判斷當(dāng)前線程是不是主線程

知道了上面兩個(gè)問(wèn)題,這個(gè)問(wèn)題就好回答了

方法一:

Looper.myLooper() == Looper.getMainLooper()

方法二:

Looper.getMainLooper().getThread() == Thread.currentThread()

方法三: 方法二的簡(jiǎn)化版

Looper.getMainLooper().isCurrentThread()

Q:Looper.loop() 會(huì)退出嗎?

不會(huì)自動(dòng)退出,但是我們可以通過(guò) Looper#quit() 或者 Looper#quitSafely() 讓它退出。

兩個(gè)方法都是調(diào)用了 MessageQueue#quit(boolean) 方法,當(dāng) MessageQueue#next() 方法發(fā)現(xiàn)已經(jīng)調(diào)用過(guò) MessageQueue#quit(boolean) 時(shí)會(huì) return null 結(jié)束當(dāng)前調(diào)用,否則的話即使 MessageQueue 已經(jīng)是空的了也會(huì)阻塞等待。

Q:MessageQueue#next 在沒(méi)有消息的時(shí)候會(huì)阻塞,如何恢復(fù)?

當(dāng)其他線程調(diào)用 MessageQueue#enqueueMessage 時(shí)會(huì)喚醒 MessageQueue,這個(gè)方法會(huì)被 Handler#sendMessageHandler#post 等一系列發(fā)送消息的方法調(diào)用。

boolean enqueueMessage(Message msg, long when) {
    // 略
    synchronized (this) {
        // 略
        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 {
            // 略
        }
        if (needWake) {
            nativeWake(mPtr); // 喚醒
        }
    }
    return true;
}

Q:Looper.loop() 方法是一個(gè)死循環(huán)為什么不會(huì)阻塞APP

我認(rèn)為更好的回答:
這是一個(gè)假象,舉個(gè)例子

public static void main(String[] args){
    while(true){
        // do work in while
    }
    doSomeThingOutWhile();
}

對(duì)于從整個(gè)main方法來(lái)看,while(true) 確實(shí)阻塞了 doSomeThingOutWhile() 這個(gè)方法的執(zhí)行,對(duì)于這樣看,好像確實(shí)是卡住了,因?yàn)槲覀冊(cè)?doSomeThingOutWhile 方法中想要做的事沒(méi)法做了,但是如果我們把我們要做的事情通過(guò)隊(duì)列放到 while 里面去做,那么是不是你就不會(huì)覺(jué)得卡了,你想要做的事情都完成了,雖然有個(gè)死循環(huán)但并不影響你想要做什么,而Android中 Looper.loop() 就是這樣的原理,因?yàn)樗凶屛覀儠?huì)覺(jué)得卡住的都被放到 MessageQueue 里,然后通過(guò)Looper取出并交給 Handler執(zhí)行了。

PS:不僅僅是Android,幾乎所有和UI操作的都有一個(gè)類(lèi)似Android Handler機(jī)制的事件循環(huán)處理機(jī)制

-----分割線-------

下面是原始回答,會(huì)讓人覺(jué)得卡是因?yàn)樗姥h(huán)之后的代碼無(wú)法執(zhí)行,如果沒(méi)有理解到其實(shí)我們的代碼都是執(zhí)行在死循環(huán)里面的話,還是沒(méi)有辦法理解為什么不會(huì)卡。

如果說(shuō)操作系統(tǒng)是由中斷驅(qū)動(dòng)的,那么Android的應(yīng)用在宏觀上可以說(shuō)是 Handler 機(jī)制驅(qū)動(dòng)的,所以主線程中的 Looper 不會(huì)一直阻塞的,原因如下(以下是我瞎JB猜的,歡迎補(bǔ)充、指正):

  • 當(dāng)隊(duì)列中只有延遲消息的時(shí)候,阻塞的時(shí)間等于頭結(jié)點(diǎn)的 when 減去 當(dāng)前時(shí)間,時(shí)間到了以后會(huì)自動(dòng)喚醒。
  • 在Android中 一個(gè)進(jìn)程中不會(huì)只有一個(gè)線程,由于 Handler 的機(jī)制,導(dǎo)致我們?nèi)绻僮?View 等都要通過(guò) Handler 將事件發(fā)送到主線程中去,所以會(huì)喚醒阻塞。
  • 傳感器的事件,如:觸摸事件、鍵盤(pán)輸入等。
  • 繪制事件:我們知道要想顯示流暢那么屏幕必須保持 60fps的刷新率,那繪制事件在入隊(duì)列時(shí)也會(huì)喚醒。
  • 總是有Message 源源不斷的被加入到 MessageQueue 中去,事件是一環(huán)扣一環(huán)的,舉個(gè) Fragment 的栗子:
getSupportFragmentManager()
        .beginTransaction()
        .replace(android.R.id.content,new MyFragment())
        .commit();

這個(gè)時(shí)候并不是立馬把 MyFragment顯示出來(lái)了,而是經(jīng)過(guò)層層的調(diào)用來(lái)到了 FragmentManager#scheduleCommit() 方法,在這里又有入隊(duì)列操作,

// FragmentManager.java:2103
private void scheduleCommit() {
    synchronized (this) {
        boolean postponeReady =
                mPostponedTransactions != null && !mPostponedTransactions.isEmpty();
        boolean pendingReady = mPendingActions != null && mPendingActions.size() == 1;
        if (postponeReady || pendingReady) {
            mHost.getHandler().removeCallbacks(mExecCommit);
            mHost.getHandler().post(mExecCommit); // 這里有入隊(duì)列操作
        }
    }
}

提交后是不是緊接著又是一系列的生命周期的事件分發(fā)?所以。。。

你還有什么關(guān)于Handler的問(wèn)題,評(píng)論告訴我吧

如果你還有什么在面試中遇到的和 Handler 相關(guān)的問(wèn)題,該博客中沒(méi)有體現(xiàn)出來(lái)的趕緊評(píng)論告訴我吧,我會(huì)持續(xù)補(bǔ)充到這篇博客當(dāng)中。


我最近剛剛開(kāi)通了微信公眾號(hào)(怪盜kidou),歡迎關(guān)注

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡(jiǎn)書(shū)系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。
  • 序言:七十年代末,一起剝皮案震驚了整個(gè)濱河市,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌,老刑警劉巖,帶你破解...
    沈念sama閱讀 228,546評(píng)論 6 533
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場(chǎng)離奇詭異,居然都是意外死亡,警方通過(guò)查閱死者的電腦和手機(jī),發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 98,570評(píng)論 3 418
  • 文/潘曉璐 我一進(jìn)店門(mén),熙熙樓的掌柜王于貴愁眉苦臉地迎上來(lái),“玉大人,你說(shuō)我怎么就攤上這事。” “怎么了?”我有些...
    開(kāi)封第一講書(shū)人閱讀 176,505評(píng)論 0 376
  • 文/不壞的土叔 我叫張陵,是天一觀的道長(zhǎng)。 經(jīng)常有香客問(wèn)我,道長(zhǎng),這世上最難降的妖魔是什么? 我笑而不...
    開(kāi)封第一講書(shū)人閱讀 63,017評(píng)論 1 313
  • 正文 為了忘掉前任,我火速辦了婚禮,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘。我一直安慰自己,他們只是感情好,可當(dāng)我...
    茶點(diǎn)故事閱讀 71,786評(píng)論 6 410
  • 文/花漫 我一把揭開(kāi)白布。 她就那樣靜靜地躺著,像睡著了一般。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上,一...
    開(kāi)封第一講書(shū)人閱讀 55,219評(píng)論 1 324
  • 那天,我揣著相機(jī)與錄音,去河邊找鬼。 笑死,一個(gè)胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播,決...
    沈念sama閱讀 43,287評(píng)論 3 441
  • 文/蒼蘭香墨 我猛地睜開(kāi)眼,長(zhǎng)吁一口氣:“原來(lái)是場(chǎng)噩夢(mèng)啊……” “哼!你這毒婦竟也來(lái)了?” 一聲冷哼從身側(cè)響起,我...
    開(kāi)封第一講書(shū)人閱讀 42,438評(píng)論 0 288
  • 序言:老撾萬(wàn)榮一對(duì)情侶失蹤,失蹤者是張志新(化名)和其女友劉穎,沒(méi)想到半個(gè)月后,有當(dāng)?shù)厝嗽跇?shù)林里發(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 48,971評(píng)論 1 335
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 40,796評(píng)論 3 354
  • 正文 我和宋清朗相戀三年,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點(diǎn)故事閱讀 42,995評(píng)論 1 369
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情,我是刑警寧澤,帶...
    沈念sama閱讀 38,540評(píng)論 5 359
  • 正文 年R本政府宣布,位于F島的核電站,受9級(jí)特大地震影響,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 44,230評(píng)論 3 347
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧,春花似錦、人聲如沸。這莊子的主人今日做“春日...
    開(kāi)封第一講書(shū)人閱讀 34,662評(píng)論 0 26
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)。三九已至,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背。 一陣腳步聲響...
    開(kāi)封第一講書(shū)人閱讀 35,918評(píng)論 1 286
  • 我被黑心中介騙來(lái)泰國(guó)打工, 沒(méi)想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留,地道東北人。 一個(gè)月前我還...
    沈念sama閱讀 51,697評(píng)論 3 392
  • 正文 我出身青樓,卻偏偏與公主長(zhǎng)得像,于是被迫代替她去往敵國(guó)和親。 傳聞我的和親對(duì)象是個(gè)殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 47,991評(píng)論 2 374

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