Android Handler機(jī)制探索及原理分析

? ? ? ? Handler在android開發(fā)中占有舉足輕重的位置胆数,相信大家都熟悉其用法及基本使用。

Handler是什么互墓?

? ? ? ? Handler是安卓提供的一種消息機(jī)制必尼。通常用于接受子線程發(fā)送的數(shù)據(jù),并用此數(shù)據(jù)配合主線程更新UI。

為什么要用Handler?

? ? ? ? 舉個例子篡撵,我們點擊一個按鈕去服務(wù)器請求數(shù)據(jù)判莉。如果直接在主線程(UI線程)做請求操作,界面會出現(xiàn)假死現(xiàn)象育谬, 如果長時間還沒有完成的話券盅,會收到Android系統(tǒng)的一個錯誤提示? "應(yīng)用程序無響應(yīng)(ANR)"。為什么呢膛檀?因為在Android里, App的響應(yīng)能力是由Activity Manager和Window Manager系統(tǒng)服務(wù)來監(jiān)控的. 通常在如下三種情況下會彈出ANR對話框:

1:KeyDispatchTimeout(谷歌default 5s锰镀,MTK平臺上是8s) --主要類型? 按鍵或觸摸事件在特定時間內(nèi)無響應(yīng)

2:BroadcastTimeout(10s)? BroadcastReceiver在特定時間內(nèi)無法處理完成

3:ServiceTimeout(20s) --小概率類型? Service在特定的時間內(nèi)無法處理完成。

? ? ? ? 既然這樣咖刃,那我們就把這些耗時操作放在子線程中執(zhí)行好了泳炉。 可問題來了,需要把數(shù)據(jù)填充到相關(guān)控件中展示嚎杨。但Android中更新UI只能在主線程中更新花鹅,子線程中操作是危險的。那怎么走出這個困境枫浙,用Handler刨肃!

1.試想一下:如果在一個Activity中,有多個線程去更新UI自脯,并且都沒有加鎖機(jī)制之景,那么會產(chǎn)生什么樣的問題?——更新界面混亂膏潮。

2.是不是在Android中子線程真的不能更新UI锻狗?這也不一定,之所以子線程不能更新UI界面,是因為Android在線程的方法里面采用checkThread進(jìn)行判斷是否是主線程轻纪,而這個方法是在ViewRootImpl中的油额,這個類是在onResume里面才生成的。因此刻帚,如果這個時候子線程在onCreate方法里面生成更新UI潦嘶,而且沒有做耗時多的操作,還是可更新UI的崇众。你可以驗證一下(打開注釋和注釋那行代碼比較下):

protected void onCreate(@Nullable Bundle savedInstanceState) {

? ? ? ? ......

? ? ? new Thread( new Runnable() {? ?

? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? @Override? ? ? ? ? ? public void run() {

? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? //步驟A try{ Thread.sleep(200)}catch(...){...};

? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? tv.setText("子線程中訪問");

? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? }

? ? ? ? ? ? ? ? ? ? ? ? }).start()掂僵;

? ? ? ? ......

}

怎么使用Handler?

? ? ? ? 我們模擬子線程網(wǎng)絡(luò)請求,完成后更新UI操作顷歌。

step1:

創(chuàng)建Handler對象 锰蓬,重寫 handleMessage 方法

step2:

子線程耗時操作 發(fā)送消息

注:代碼中Message為Handler接收與處理的消息對象。?

看看運(yùn)行后打印結(jié)果:

運(yùn)行結(jié)果

根據(jù)上面打印日志可以看出handlerMessage運(yùn)行在main線程眯漩,故可以在這里更新ui芹扭。而且發(fā)送的what成功正確接收到為1000.

當(dāng)然Handler還有很多其他用法比如:

1.post(Runnable)? ,postDelayed(Runnable ,long);?

2. sendMessage(Message),sendMessageDelayed(Message,long)

3.sendEmptyMessage(int),sendEmptyMessageDelayed(int,long)

....

這里就不一一列舉了赦抖。我們在開發(fā)中常用的延時操作往往借助上面 handler.xxxDelayed方法來輕松實現(xiàn)舱卡。? ? 其實使用上述不管哪種方法都調(diào)用了sendMessageDelayed函數(shù),所以實質(zhì)都是一致的队萤。我們隨便拿一個看看其內(nèi)部實現(xiàn)(如:post(Runnable) ):

handler.post(runnable)方法源碼

上面我們在主線程創(chuàng)建并使用了Handler對象轮锥,可以正常在子線程發(fā)送消息并成功接收。那么就有好事者說了“我要在子線程創(chuàng)建并使用Handler可不可以浮禾,沒別的意思交胚,就是任性想玩玩”。好吧盈电,閑話少說蝴簇,那就我們就來試試...

子線程創(chuàng)建并使用Handler

我們在子線程中試試Handler呀

實際上寫出這個還沒運(yùn)行,我掐指一算就覺得這樣會有問題(就不告訴你我提前已經(jīng)偷偷了解了匆帚,哈哈)熬词。

子線程 · handler · error

看紅框部位的報錯信息,知道是線程里創(chuàng)建Handler沒調(diào)用Looper.prepare()方法.這是什么鬼?我們定位到創(chuàng)建Handler時使用的構(gòu)造函數(shù)處:

構(gòu)造函數(shù)-Handler(Callback callback, boolean async)

也就是說Looper.myLooper()得到的對象為空吸重,就拋出了"Can't create handler inside thread that has not called Looper.prepare()"這個異常互拾。進(jìn)入Looper.myLooper()一探究竟:

Looper.myLooper()

sThreadLocal.get()返回一個空對象。那sThreadLocal又是什么?在Looper類源碼中可以看到:

sThreadLocal

sThreadLocal是一個ThreadLocal 靜態(tài)變量嚎幸。那ThreadLocal是什么有什么作用颜矿?

ThreadLocal是什么?

? ? ? ? ThreadLocal 是線程的局部變量嫉晶, 是每一個線程所單獨持有的骑疆。當(dāng)使用ThreadLocal維護(hù)變量的時候 為每一個使用該變量的線程提供一個獨立的變量副本田篇,即每個線程內(nèi)部都會有一個該變量,這樣同時多個線程訪問該變量并不會彼此相互影響箍铭,因此他們使用的都是自己從內(nèi)存中拷貝過來的變量的副本泊柬, 這樣就不存在線程安全問題。

什么意思?做個實例來體驗一下以便于理解诈火。在此之前先介紹下ThreadLocal常用方法--->

public T get() { } // 獲取ThreadLocal在當(dāng)前線程中保存的變量副本? ? ? ? ? ? ? ?

public void set(T value) { } //set()設(shè)置當(dāng)前線程中變量的副本? ? ? ? ? ? ? ? ? ? ? ? ? ?

public void remove() { } //移除當(dāng)前線程中變量的副本? ? ? ? ? ? ? ?

protected T initialValue() { } //一般是用來在使用時進(jìn)行重寫的

ThreadLocal使用實例
TheadLocal 日志

通過上面例子應(yīng)該可以檢驗“為每一個使用該變量的線程提供一個獨立的變量副本”的含義了吧兽赁。。冷守。

如果想要了解ThreadLocal內(nèi)部原理移步:Java多線程編程-(8)-多圖深入分析ThreadLocal原理

現(xiàn)在我們清楚了ThreadLocal的作用刀崖,繼續(xù)回到上面對Looper的分析。因sThreadLocal.get()返回null拍摇,導(dǎo)致異常發(fā)生蒲跨。根據(jù)錯誤日志,需要先調(diào)用 Looper.prepare()方法才行。那我們可以推測授翻,prepare()方法里面應(yīng)該做了sThreadLocal.set(looper)操作。

Looper<--->MessageQueue

那么我們具體看看Looper.prepare()了什么?

Looper.prepare()

果不其然孙咪,同我們上面推測的一致堪唐,sThreadLocal調(diào)用set方法保存了一個looper變量,同時可以知道對于每一個線程只能有一個Looper翎蹈。接下來看看Looper(boolean) 構(gòu)造函數(shù):

Looper構(gòu)造函數(shù)

也就是說我們創(chuàng)建一個Looper對象淮菠,MessageQueue (按字面意思理解:消息隊列就被創(chuàng)建了。也可以發(fā)現(xiàn)荤堪,默認(rèn)情況下合陵,這個MessageQueue的quiteAllow=true。

我們可以發(fā)現(xiàn):在一個線程中如果存在Looper則 Looper和與之關(guān)聯(lián)的MessageQueue都是唯一的澄阳。那MessageQueue是什么?它是不是與Message有些啥曖昧關(guān)系呢拥知?看著像是Messa-geQueue包養(yǎng)了一隊列的Message。(逃...)

MessageQueue 與 Message

前面我們用到并簡單介紹了下Message碎赢,現(xiàn)在再對它做一下詳細(xì)的分析...

Message截取部分代碼

前面四個what,arg1,arg2,obj屬性在用Handler經(jīng)常會用到低剔,應(yīng)該很熟悉。另外還有幾點值得注意:

Message 實現(xiàn)Parcelable 接口肮塞,也即實現(xiàn)了序列化襟齿,說明Message可用于進(jìn)程間通信。? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ?

有一個Handler 對象 target(跟我們使用的Handler有沒有關(guān)枕赵?)? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ?

有個callback的Runnable 對象(是不是想到Handler.post(runnable) 這個runnable)? ? ? ? ? ? ? ? ? ?

推薦使用obtain()猜欺,該方法可以從消息池中獲取Message實例,不推薦直接調(diào)用構(gòu)造方法拷窜。

好吧現(xiàn)在Message內(nèi)部的一些重要特征我們都已打探清楚开皿,那MessageQueue到底是什么呢?

MessageQueue構(gòu)造函數(shù)

nativeInit()方法實現(xiàn)為android_os_MessageQueue_nativeInit();這是一個底部方法在此不做細(xì)究涧黄。有興趣的自行深入分析。如果只想要了解MessageQueue的作用我們換種方式直接看看其注釋副瀑,便會一目了然:

MessageQueue 注釋

也就是說MessageQueue確實是用來存放消息(Message對象)的容器(可按字面意思理解為隊列)弓熏。

其實MessageQueue數(shù)據(jù)結(jié)構(gòu),實質(zhì)是一個單向鏈表糠睡,Message對象有個next字段保存列表中的下一個挽鞠,MessageQueue中的mMessages保存鏈表的第一個元素。

既然是“隊列”那它就有其常規(guī)操作:

入隊 :? boolean enqueueMessage(Message msg, long when) {...}

出隊:? Message next() {...}

這里先不詳細(xì)介紹等我們下面按流程分析到在說狈孔。

到此為止我們大概搞清楚:

1.如果希望Handler正常工作,在當(dāng)前線程中要有一個Looper對象

2.在初始化Looper對象時會創(chuàng)建一個與之關(guān)聯(lián)的MessageQueue;

3.MessageQueue存放并管理Message

4.Message是Handler接收與處理的消息對象

上面一步步分析信认,都可謂是為了破解“在子線程創(chuàng)建Handler”所引發(fā)的這一"血案"。至于怎么解決“子線程創(chuàng)建Handler”報出error均抽,相信通過以上講解嫁赏,很容易就大手一揮寫出以下代碼:

子線程·Handler ·+ looper.prepare

然后運(yùn)行一下,當(dāng)然沒有報任何異常.但我們發(fā)現(xiàn)也沒有任何日志打印出來油挥。也就是說我們發(fā)送的消息(sendMessage)沒有接收到潦蝇。那是什么原因?現(xiàn)在該怎么辦?此刻就有一個疑問浮現(xiàn)在腦海---“為什么主線程創(chuàng)建Handler可以正常工作?是不是它做了其他的操作?”

初遇Looper.loop()...

? ? ? ? 我們找到android應(yīng)用程序的入口ActivityThread中的main方法。ActivityThread就是應(yīng)用程序的主線程深寥,打開它的main方法可以看到:

ActivityThread? main()片段

Looper.prepareMainLooper() ?進(jìn)去看看

Looper.prepareMainLooper()

確實攘乒,Looper.prepareMainLooper() 調(diào)用了prepare()方法(注意這里調(diào)用prepare時傳遞的參數(shù)值為false,和我們之前創(chuàng)建普通Looper時是不同的惋鹅。因為這是主線程则酝,不會被允許被外部代碼終止),所以現(xiàn)在知道為什么在主線程直接創(chuàng)建Handler而不拋異常了吧。然后,后面還有個Looper.loop()?是不是就是少這步操作我們子線程Handler沒有正常工作,那加上試一下:

子線程.Handler.can.use

真的可以正常工作闰集,打印出log了!

log

那不禁要問沽讹,為什么加了個Looper.loop()就可以正常接收消息了呢?我們這里暫時不深究loop()內(nèi)部到底什么原理,可以暫按得到的效果和其字面含義理解為一個獲取消息的循環(huán)(輪詢)武鲁。至此Handler消息已經(jīng)可以正確接收爽雄,那我們不妨先看看消息是怎么發(fā)送的,因為有入才有出洞坑,我們就先從“入”這個源頭扒起盲链。

Handler發(fā)送消息實現(xiàn)原理

發(fā)送消息我們會調(diào)用handler.post(),handler.sendMessage()等方法,前文也已經(jīng)分析到所有這些方法實質(zhì)都調(diào)用的是sendMessageDelay():

sendMessageDelay

繼續(xù)看sendMessageAtTime:

sendMessageAtTime

在前文Handler的構(gòu)造函數(shù)中我們知道m(xù)Queue=mLooper.mQueue迟杂,也即是Looper中關(guān)聯(lián)的MessageQueue對象刽沾。執(zhí)行至equeueMessage:

equeueMessage

特別注意:msg.target = this 這句代碼,該message的target賦值為當(dāng)前的handler對象,這里Message就和當(dāng)前Handler關(guān)聯(lián)起來了排拷,記住msg.target很重要,后面我們會用到侧漓。

我們可以看到最后執(zhí)行的是MessageQueue的enqueueMessage方法,前面在介紹MessageQueue的時候我們就知道了這個enqueueMessage方法是用于"入隊"操作的监氢〔颊幔看看源碼也就清楚:

enqueueMessage部分源碼

參數(shù)msg是由我們傳進(jìn)去Message對象藤违,when時是執(zhí)行時間,細(xì)心的朋友會發(fā)現(xiàn)mMessages這個對象,我們可以把它理解為是待執(zhí)行的message隊列纵揍,該隊列是按照when的時間排序的且第一個消息是最先執(zhí)行顿乒。

代碼第4行中有三個條件:如果mMessages對象為空,或when為0也就是立刻執(zhí)行泽谨,或者新消息的when時間比mMessages隊列的when時間還要早璧榄,符合以上任一條件就把新的msg插到mMessages的前面 并把next指向它,也就是msg會插進(jìn)隊列的最前面吧雹,等待loop的輪詢骨杂。

如果上面的條件都不符合就進(jìn)入else中,我們可以看到17行是有個for的死循環(huán)遍歷已有的message對象雄卷,其中第20行有個if語句when < p.when when是新消息的執(zhí)行時間搓蚪,p.when的是隊列中message消息的執(zhí)行時間,如果找到比新的message還要晚執(zhí)行的消息丁鹉,就執(zhí)行

msg.next = p;?

prev.next = msg;

也就是把插到該消息的前面妒潭,優(yōu)先執(zhí)行新的消息。

現(xiàn)在我們搞清楚了sendMessage最終就是將消息(Message)放入MessageQueue里面揣钦,由其存放并管理杜耙,那接下來我們要明白的一點就是怎么取出消息了。那就回到了Looper.loop()的解釋了拂盯。

消息接收:再續(xù)Looper.loop()

looper.loop()片段-1


looper.loop()片段-2

片段1好理解,我們重點分析片段2 记靡;

這里要給大家說一下谈竿,Linux的一個進(jìn)程間通信機(jī)制:管道(pipe)。

原理:在內(nèi)存中有一個特殊的文件摸吠,這個文件有兩個句柄(引用)空凸,一個是讀取句柄,一個是寫入句柄

主線程Looper從消息隊列讀取消息寸痢,當(dāng)讀完所有消息時呀洲,進(jìn)入睡眠,主線程阻塞啼止。子線程往消息隊列發(fā)送消息道逗,并且往管道文件寫數(shù)據(jù),主線程即被喚醒献烦,從管道文件讀取數(shù)據(jù)滓窍,主線程被喚醒只是為了讀取消息,當(dāng)消息讀取完畢巩那,再次睡眠吏夯。所以不會過多消耗性能此蜈。

looper里面是一個for死循環(huán),看畫紅線的第一行代碼,是從MessageQueue中提取Message,注釋可能造成阻塞噪生。我們進(jìn)到MessageQueue的next里面看看:

MessageQueue · next 部分代碼

簡單分析一下裆赵,if(now < msg.when)? 若當(dāng)前時間還沒到msg指定時間,設(shè)置一個timeout以到時用于喚醒跺嗽;else msg執(zhí)行出隊操作战授。另外可以發(fā)現(xiàn)當(dāng)msg=null,當(dāng)也即消息隊列為空抛蚁,nextPollTimeoutMillis置為 -1 陈醒,next就會阻塞。

回到loop()方法片段2瞧甩,我們得到了Message對象钉跷,繼續(xù)向下走到第二處紅線位置,也就到了分發(fā)處理Message的時候了肚逸。

? ? ? ? msg.target.dispatchMessage(msg);

記不記得我們分析發(fā)送消息時提到的一個"特別注意"事項(msg.target = this 這句代碼,該message的target賦值為當(dāng)前的handler對象),在這里果然用到了爷辙。也就是說Handler發(fā)送的每個Message對象都存在有該Handler的句柄(target),所以這里實質(zhì)就是調(diào)用了handler的dispatchMessage方法,那我們進(jìn)入Handler.dispatch方法:

Handler.dispatch

現(xiàn)在該明白了為什么我們在使用Handler的時候復(fù)寫其handlerMessage方法朦促,可以在內(nèi)部接收并處理消息了吧膝晾。是不是有種柳暗花明的感覺。

到此為止务冕,我們已經(jīng)把Handler的整個工作原理擼得差不多了血当,大致梳理一下。

當(dāng)我們使用Handler時禀忆,在當(dāng)前線程必須有且僅有一個Looper對象臊旭,Looper里面維護(hù)一個MessageQueue。所以需要提前調(diào)用Looper.prepare()方法將loop對象設(shè)置到內(nèi)部靜態(tài)ThreadLocal中(以保證線程安全)箩退。(主線程已設(shè)置了)

我們用Handler主要是發(fā)送和接收消息离熏。

對于發(fā)送消息:通過post,send等方法(實質(zhì)都是send)發(fā)送消息Message(runnable最后也轉(zhuǎn)化為message)實現(xiàn)。最終都是調(diào)用MessageQueue的enqueueMessage方法將Message插入MessageQueue(上面所說Looper中所維護(hù)的)這一單向鏈表中進(jìn)行統(tǒng)一管理戴涝。

接收消息:我們必須開啟Looper.loop()來輪詢滋戳,通過調(diào)用MessageQueue中的next()方法移除并獲取之前發(fā)送的Message對象(MessageQueue為空時,next阻塞)。獲得的Message對象最終交由發(fā)送該消息的Handler對象(message.target)的dispatchMessage方法處理啥刻。而dispatchMessage方法最后會走到handlerMessage()方法去奸鸯。所以我們在創(chuàng)建Handler時才能夠在其handlerMessage()或callback.

handlerMessage方法中獲取發(fā)送的消息并做出系列操作。

那么至此不經(jīng)要問可帽,主線程中的Looper.loop()一直無限循環(huán)為什么不會造成ANR府喳?

主線程-loop()-anr

首先,我們想一下蘑拯,主線程要沒有Looper.loop()會怎么樣?

顯而易見钝满,如果應(yīng)用入口main方法中沒有l(wèi)ooper進(jìn)行循環(huán)脉让,那么主線程一運(yùn)行完畢就會退出柬帕。那我們打開一個app就過段時間直接關(guān)閉了。也就是說:ActivityThread的main方法主要就是做消息循環(huán),一旦退出消息循環(huán)寿冕,那么你的應(yīng)用也就退出了唐础。

我們知道了looper.loop()是必須的彤枢,再討論這個死循環(huán)為啥不會造成ANR異常惨篱?

? ? ? 因為Android 的是由事件驅(qū)動的,looper.loop() 會不斷地接收/處理事件收厨,每一個點擊觸摸或者說Activity的生命周期都是運(yùn)行在 Looper.loop() 的控制之下晋柱,如果loop停止,那應(yīng)用也就停止了诵叁。所以只能是某一個消息或者說對消息的處理時間超過系統(tǒng)規(guī)定時長(阻塞)才會導(dǎo)致ANR,而不是looper.loop()本身雁竞。


end...


因個人水平有限,有紕漏或錯誤處還望大家批評指正,謝謝拧额!

參考:

終于明白了Handler的運(yùn)行機(jī)制 - CSDN博客

主線程中的Looper.loop()一直無限循環(huán)為什么不會造成ANR碑诉? - CSDN博客

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市侥锦,隨后出現(xiàn)的幾起案子进栽,更是在濱河造成了極大的恐慌,老刑警劉巖恭垦,帶你破解...
    沈念sama閱讀 216,402評論 6 499
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件快毛,死亡現(xiàn)場離奇詭異,居然都是意外死亡番挺,警方通過查閱死者的電腦和手機(jī)祸泪,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 92,377評論 3 392
  • 文/潘曉璐 我一進(jìn)店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來建芙,“玉大人,你說我怎么就攤上這事懂扼〗” “怎么了?”我有些...
    開封第一講書人閱讀 162,483評論 0 353
  • 文/不壞的土叔 我叫張陵阀湿,是天一觀的道長赶熟。 經(jīng)常有香客問我,道長陷嘴,這世上最難降的妖魔是什么映砖? 我笑而不...
    開封第一講書人閱讀 58,165評論 1 292
  • 正文 為了忘掉前任,我火速辦了婚禮灾挨,結(jié)果婚禮上邑退,老公的妹妹穿的比我還像新娘竹宋。我一直安慰自己,他們只是感情好地技,可當(dāng)我...
    茶點故事閱讀 67,176評論 6 388
  • 文/花漫 我一把揭開白布蜈七。 她就那樣靜靜地躺著,像睡著了一般莫矗。 火紅的嫁衣襯著肌膚如雪飒硅。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 51,146評論 1 297
  • 那天作谚,我揣著相機(jī)與錄音三娩,去河邊找鬼。 笑死妹懒,一個胖子當(dāng)著我的面吹牛雀监,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播彬伦,決...
    沈念sama閱讀 40,032評論 3 417
  • 文/蒼蘭香墨 我猛地睜開眼滔悉,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了单绑?” 一聲冷哼從身側(cè)響起回官,我...
    開封第一講書人閱讀 38,896評論 0 274
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎搂橙,沒想到半個月后歉提,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 45,311評論 1 310
  • 正文 獨居荒郊野嶺守林人離奇死亡区转,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 37,536評論 2 332
  • 正文 我和宋清朗相戀三年苔巨,在試婚紗的時候發(fā)現(xiàn)自己被綠了。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片废离。...
    茶點故事閱讀 39,696評論 1 348
  • 序言:一個原本活蹦亂跳的男人離奇死亡侄泽,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出蜻韭,到底是詐尸還是另有隱情悼尾,我是刑警寧澤,帶...
    沈念sama閱讀 35,413評論 5 343
  • 正文 年R本政府宣布肖方,位于F島的核電站闺魏,受9級特大地震影響,放射性物質(zhì)發(fā)生泄漏俯画。R本人自食惡果不足惜析桥,卻給世界環(huán)境...
    茶點故事閱讀 41,008評論 3 325
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧泡仗,春花似錦埋虹、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,659評論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至峦树,卻和暖如春辣辫,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背魁巩。 一陣腳步聲響...
    開封第一講書人閱讀 32,815評論 1 269
  • 我被黑心中介騙來泰國打工急灭, 沒想到剛下飛機(jī)就差點兒被人妖公主榨干…… 1. 我叫王不留,地道東北人谷遂。 一個月前我還...
    沈念sama閱讀 47,698評論 2 368
  • 正文 我出身青樓葬馋,卻偏偏與公主長得像,于是被迫代替她去往敵國和親肾扰。 傳聞我的和親對象是個殘疾皇子畴嘶,可洞房花燭夜當(dāng)晚...
    茶點故事閱讀 44,592評論 2 353

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