Handler機制分析

Android消息處理機制實際上是一種異步處理機制筏勒。應(yīng)用程序通過消息來驅(qū)動移迫,系統(tǒng)為每一個應(yīng)用程序維護一個消息隊例,應(yīng)用程序的主線程不斷地從這個消息隊例中獲取消息(Looper)管行,然后對這些消息進行處理(Handler)厨埋,這樣就實現(xiàn)了通過消息來驅(qū)動應(yīng)用程序的執(zhí)行.在Android源碼中,ActivityManagerService和Activity捐顷、Service 通信的時候揽咕,都是通過Binder通知應(yīng)用程序,然后程序拿到通知后并不是馬上處理消息而是將這個請求封裝成一個消息Message套菜,然后把這個消息放在應(yīng)用程序的消息隊列中MessageQueue中去,然后再通過消息循環(huán)Lpiper 來處理這個消息设易。這個場景算是消息循環(huán)模型的實例逗柴。這樣做的好處就是消息的發(fā)送方只要把消息發(fā)送到應(yīng)用程序的消息隊列中去就行了,它可以馬上返回去處理別的事情顿肺,而不需要等待消息的接收方去處理完這個消息才返回戏溺,這樣就可以提高系統(tǒng)的并發(fā)性。
以下是基于Android5.0以上源碼分析屠尊。末尾會分析與Androdi4.0的不同

這里主要用到了Looper.java旷祸、MessageQueue.java、NativeMessageQueue.cpp讼昆、Looper.cpp這四個類文件
調(diào)用流程.png
當系統(tǒng)加載Activity之前托享,首先ActivityManagerService 會為當前Activity創(chuàng)建一個ActivityThread,并執(zhí)行其main函數(shù)。函數(shù)中圖上兩個函數(shù)完成了消息循環(huán)的創(chuàng)建浸赫。
public final class ActivityThread {
    public static void main(String[] args) { 
         SamplingProfilerIntegration.start();
           ..........
         Process.setArgV0("");

         Looper.prepareMainLooper();

        ActivityThread thread = new ActivityThread();
         thread.attach(false);
         if (sMainThreadHandler == null) {
            sMainThreadHandler = thread.getHandler();
          }
        AsyncTask.init();
        if (false) {
             Looper.myLooper().setMessageLogging(new LogPrinter(Log.DEBUG,             "ActivityThread"));
        }

       Looper.loop();

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

我們再來看看 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();/*sThreadLocal.get()*/
        }
    }
 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));
    }

我們可以看到這個函數(shù)的主要作用是創(chuàng)建了Looper.對象并保存在自己線程的私有存儲空間內(nèi)闰围,sthreadLocal可以理解成線程內(nèi)部私有空間,不與其他線程共享既峡,每個線程只有一個羡榴。順便多說一下有sThreadLocal.get()就必有sThreadLocal.set()。這樣就保證了每一個調(diào)用 Looper.prepareMainLooper()函數(shù)的線程都有一個獨立的Looper對象运敢,我們經(jīng)常在開發(fā)中遇到的報錯

java.lang.RuntimeException: Can't create handler inside thread that has not called Looper.prepare()

就是因為在子線程中使用了Hanler對象校仑,但是沒有手動調(diào)用Looper.prepareMainLooper()忠售,導(dǎo)致子線程中并沒有Looper對象。而在UI主線程中系統(tǒng)會首先調(diào)用ActivityThread.main()進而自動調(diào)用prepareMainLooper()創(chuàng)建Looper對象迄沫。接下來我們著重看下Looper和MessageQueue稻扬。

public final class Looper {
    static final ThreadLocal<Looper> sThreadLocal = new ThreadLocal<Looper>();
    private static Looper sMainLooper;  // guarded by Looper.class
    final MessageQueue mQueue;
       .............
    private Looper(boolean quitAllowed) {
        mQueue = new MessageQueue(quitAllowed);
        mThread = Thread.currentThread();
    }
}
public final class MessageQueue {
   @SuppressWarnings("unused")
    private long mPtr; // used by native code
    Message mMessages;
      ......
    MessageQueue(boolean quitAllowed) {
        mQuitAllowed = quitAllowed;
        mPtr = nativeInit();/*JNI調(diào)用*/
    }
}

/*android_os_MessageQueue.cpp*/
static jlong android_os_MessageQueue_nativeInit(JNIEnv* env, jclass clazz) {
    NativeMessageQueue* nativeMessageQueue = new NativeMessageQueue();
    if (!nativeMessageQueue) {
        jniThrowRuntimeException(env, "Unable to allocate native queue");
        return 0;
    }
    nativeMessageQueue->incStrong(env);
    return reinterpret_cast<jlong>(nativeMessageQueue);
}
/* /framework/native/services/surfaceflinger/MessageQueue.cpp  */
NativeMessageQueue::NativeMessageQueue() : mInCallback(false), mExceptionObj(NULL) {
    mLooper = Looper::getForThread();
    if (mLooper == NULL) {
        mLooper = new Looper(false);
        Looper::setForThread(mLooper);
    }
}

/* /system/core/libutils/Looper.cpp */
Looper::Looper(bool allowNonCallbacks) :
        mAllowNonCallbacks(allowNonCallbacks), mSendingMessage(false),
        mPolling(false), mEpollFd(-1), mEpollRebuildRequired(false),
        mNextRequestSeq(0), mResponseIndex(0), mNextMessageUptime(LLONG_MAX) {
    mWakeEventFd = eventfd(0, EFD_NONBLOCK);
    LOG_ALWAYS_FATAL_IF(mWakeEventFd < 0, "Could not make wake event fd.  errno=%d", errno);
    AutoMutex _l(mLock);
    rebuildEpollLocked();
}

void Looper::rebuildEpollLocked() {
    // Close old epoll instance if we have one.
    if (mEpollFd >= 0) {
#if DEBUG_CALLBACKS
        ALOGD("%p ~ rebuildEpollLocked - rebuilding epoll set", this);
#endif
        close(mEpollFd);
    }

    // Allocate the new epoll instance and register the wake pipe.
    mEpollFd = epoll_create(EPOLL_SIZE_HINT);
    LOG_ALWAYS_FATAL_IF(mEpollFd < 0, "Could not create epoll instance.  errno=%d", errno);

    struct epoll_event eventItem;
    memset(& eventItem, 0, sizeof(epoll_event)); // zero out unused members of data field union
    eventItem.events = EPOLLIN;
    eventItem.data.fd = mWakeEventFd;
    int result = epoll_ctl(mEpollFd, EPOLL_CTL_ADD, mWakeEventFd, & eventItem);
    LOG_ALWAYS_FATAL_IF(result != 0, "Could not add wake event fd to epoll instance.  errno=%d",errno);
      .........
}

Looper new了一個MessageQueue,保存在自己的成員變量mQueue中邢滑,MessageQueue 創(chuàng)建了一個NativeMessageQueue腐螟,并把其在內(nèi)存中的地址記錄在mPtr中。在natvive層Looper中這里我們只需關(guān)注三個函數(shù)即可

1.mWakeEventFd = eventfd(0, EFD_NONBLOCK);
2.mEpollFd = epoll_create(EPOLL_SIZE_HINT);
3.epoll_ctl(mEpollFd, EPOLL_CTL_ADD, mWakeEventFd, & eventItem);

了解Linux C的同學(xué)應(yīng)該知道這是linux 的epoll機制和eventfd 信號量困后,

http://www.reibang.com/p/27befc4dd33a //參考eventfd 和epoll

這里所做的事情就是handler實現(xiàn)延時的核心乐纸,非常重要
首先通過eventfd在創(chuàng)建了一個文件描述符,然后在rebuildEpollLocked函數(shù)中 通過epoll_ctl告訴mEpollFd監(jiān)聽mWakeEventFd文件描述符中的EPOLLIN事件摇予,當mWakeEventFd里面有內(nèi)容的時候汽绢,喚醒epoll_wait()
到這里Handler前期的準備工作已經(jīng)做好

/* Looper.java */
 public static void loop() {
        final Looper me = myLooper();//獲取java層Looper對象
                ..............
      /*獲取Looper對象中保存的MessageQueue對象*/
        final MessageQueue queue = me.mQueue;
        for (;;) {
          /*當我們使用Handler發(fā)送Message的時候,
            實際上系統(tǒng)是把消息插入到消息隊列中按照時間先后重新排列*/
            Message msg = queue.next(); // might block
            if (msg == null) {
                // No message indicates that the message queue is quitting.
                return;
            }
          ....................
            msg.target.dispatchMessage(msg);
          ....................
        }
    }
    
/* MessageQueue.java*/
 Message next() {
        // Return here if the message loop has already quit and been disposed.
        // This can happen if the application tries to restart a looper after quit
        // which is not supported.
        final long ptr = mPtr;
        if (ptr == 0) {
            return null;
        }

        int pendingIdleHandlerCount = -1; // -1 only during first iteration
        int nextPollTimeoutMillis = 0;
        for (;;) {
            if (nextPollTimeoutMillis != 0) {
                Binder.flushPendingCommands();
            }

            nativePollOnce(ptr, nextPollTimeoutMillis);
/*組建消息隊列*/
            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.
          /*獲取下條Message的超時時間*/
                        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;
                }

              .........................
     
            // While calling an idle handler, a new message could have been delivered
            // so go back and look again for a pending message without waiting.
            nextPollTimeoutMillis = 0;
        }
    }  

/*Looper.cpp*/
int Looper::pollOnce(int timeoutMillis, int* outFd, int* outEvents, void** outData) {
    int result = 0;
    for (;;) {
           ......
#if DEBUG_POLL_AND_WAKE
             .......
#endif
             ......
        result = pollInner(timeoutMillis);
    }
}

int Looper::pollInner(int timeoutMillis)
{
            ......
    // Adjust the timeout based on when the next message is due.
    if (timeoutMillis != 0 && mNextMessageUptime != LLONG_MAX) {
        nsecs_t now = systemTime(SYSTEM_TIME_MONOTONIC);
        int messageTimeoutMillis = toMillisecondTimeoutDelay(now, mNextMessageUptime);
        if (messageTimeoutMillis >= 0
                && (timeoutMillis < 0 || messageTimeoutMillis < timeoutMillis)) {
            timeoutMillis = messageTimeoutMillis;
        }
    }

    // Poll.
    int result = POLL_WAKE;
    mResponses.clear();
    mResponseIndex = 0;

    // We are about to idle.
    mPolling = true;

    struct epoll_event eventItems[EPOLL_MAX_EVENTS];

      /*******************關(guān)鍵的    epoll_wait     **************/

    int eventCount = epoll_wait(mEpollFd, eventItems, EPOLL_MAX_EVENTS, timeoutMillis);

    // No longer idling.
    mPolling = false;

    // Acquire lock.
    mLock.lock();

    // Rebuild epoll set if needed.
    if (mEpollRebuildRequired) {
        mEpollRebuildRequired = false;
        rebuildEpollLocked();
        goto Done;
    }

    // Check for poll error.
    if (eventCount < 0) {
        if (errno == EINTR) {
            goto Done;
        }
        ALOGW("Poll failed with an unexpected error, errno=%d", errno);
        result = POLL_ERROR;
        goto Done;
    }

    // Check for poll timeout.
    if (eventCount == 0) {
#if DEBUG_POLL_AND_WAKE
        ALOGD("%p ~ pollOnce - timeout", this);
#endif
        result = POLL_TIMEOUT;
        goto Done;
    }

    // Handle all events.
#if DEBUG_POLL_AND_WAKE
    ALOGD("%p ~ pollOnce - handling events from %d fds", this, eventCount);
#endif

    for (int i = 0; i < eventCount; i++) {
        int fd = eventItems[i].data.fd;
        uint32_t epollEvents = eventItems[i].events;
        if (fd == mWakeEventFd) {
            if (epollEvents & EPOLLIN) {
                awoken();
            } else {
                ALOGW("Ignoring unexpected epoll events 0x%x on wake event fd.", epollEvents);
            }
        } else {
            ssize_t requestIndex = mRequests.indexOfKey(fd);
            if (requestIndex >= 0) {
                int events = 0;
                if (epollEvents & EPOLLIN) events |= EVENT_INPUT;
                if (epollEvents & EPOLLOUT) events |= EVENT_OUTPUT;
                if (epollEvents & EPOLLERR) events |= EVENT_ERROR;
                if (epollEvents & EPOLLHUP) events |= EVENT_HANGUP;
                pushResponse(events, mRequests.valueAt(requestIndex));
            } else {
                ALOGW("Ignoring unexpected epoll events 0x%x on fd %d that is "
                        "no longer registered.", epollEvents, fd);
            }
        }
    }
Done: ;
    
  ......
  
    return result;
}
Looper循環(huán)流程.png

綜上侧戴,可以看出Handler消息循環(huán)主要是通過eventfd和epoll宁昭,使線程阻塞在epoll_wait中,當文件eventfd中有消息或者epoll_wait設(shè)置的超時時間到了后酗宋,程序就往下執(zhí)行积仗,讀取消息。
現(xiàn)在我們就來看看Handler是如何發(fā)送消息喚醒線程的
首先來看Handler的兩個關(guān)鍵成員變量

 final MessageQueue mQueue;  //通過Looper.prepareMainLooper一步步創(chuàng)建的java MessageQueue  對象
 final Looper mLooper;//通過Looper.prepareMainLooper創(chuàng)建的java Looper對象
Handler發(fā)送消息流程.png
/*  MessageQueue.java */
 boolean enqueueMessage(Message msg, long when) {
        if (msg.target == null) {
            throw new IllegalArgumentException("Message must have a target.");
        }
        if (msg.isInUse()) {
            throw new IllegalStateException(msg + " This message is already in use.");
        }

        synchronized (this) {
            if (mQuitting) {
                IllegalStateException e = new IllegalStateException(
                        msg.target + " sending message to a Handler on a dead thread");
                Log.w(TAG, e.getMessage(), e);
                msg.recycle();
                return false;
            }
//組建消息隊列 _____start
            msg.markInUse();
            msg.when = when;
            Message p = mMessages;
            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 {
                // Inserted within the middle of the queue.  Usually we don't have to wake
                // up the event queue unless there is a barrier at the head of the queue
                // and the message is the earliest asynchronous message in the queue.
                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;
            }
//組建消息隊列_______end
            // We can assume mPtr != 0 because mQuitting is false.
            if (needWake) {
                nativeWake(mPtr);
            }
        }
        return true;
    }

handler 發(fā)過來的消息,首先會組建成一個消息隊列蜕猫,然后判斷時間寂曹,如果時間到了需要喚醒就執(zhí)行 nativeWake(mPtr);最終調(diào)用Looper.cpp中的wake函數(shù)

void Looper::wake() {
#if DEBUG_POLL_AND_WAKE
    ALOGD("%p ~ wake", this);
#endif

    uint64_t inc = 1;
    ssize_t nWrite = TEMP_FAILURE_RETRY(write(mWakeEventFd, &inc, sizeof(uint64_t)));
    if (nWrite != sizeof(uint64_t)) {
        if (errno != EAGAIN) {
            ALOGW("Could not write wake signal, errno=%d", errno);
        }
    }
}

驚訝不!回右!系統(tǒng)知識向mWakeEventFd 寫了一個數(shù)字隆圆,當然這個數(shù)字沒有什么意義,僅僅起了喚醒線程的作用翔烁。它只是告訴epoll_wait mWakeEventFd 這個文件描述符里面有內(nèi)容可讀渺氧,epoll_wait 擺脫阻塞繼續(xù)向下執(zhí)行,進而喚醒線程蹬屹,一步步的向上返回消息最終

/*Looper.java*/
    public static void loop() {
          ......
        for (;;) {
            Message msg = queue.next(); // might block
            if (msg == null) {
                // No message indicates that the message queue is quitting.
                return;
            }
            msg.target.dispatchMessage(msg);
            ......
        }


/***Handler.java*/
public void dispatchMessage(Message msg) {
        if (msg.callback != null) {
            handleCallback(msg);
        } else {
            if (mCallback != null) {
                if (mCallback.handleMessage(msg)) {
                    return;
                }
            }
            handleMessage(msg);
        }
    }
    ```
msg.target就是咱們創(chuàng)建的Handler對象侣背,所以調(diào)用handler的dispatchMessage函數(shù),往下就是咱們熟悉的handler過程了慨默,如果有回調(diào)函數(shù)就掉回調(diào)秃踩,沒有的話就執(zhí)行handleMessage。
整個消息循環(huán)圖.png

Anrodid4.0
與Android5.0及以上 相似度95%业筏,唯一不同的是Native層Looper.cpp中不是采用eventfd文件描述符憔杨,而是管道

Looper::Looper(bool allowNonCallbacks) :
        mAllowNonCallbacks(allowNonCallbacks), mSendingMessage(false),
        mResponseIndex(0), mNextMessageUptime(LLONG_MAX) {
    int wakeFds[2];
    int result = pipe(wakeFds);
    LOG_ALWAYS_FATAL_IF(result != 0, "Could not create wake pipe.  errno=%d", errno);

    mWakeReadPipeFd = wakeFds[0];
    mWakeWritePipeFd = wakeFds[1];

    result = fcntl(mWakeReadPipeFd, F_SETFL, O_NONBLOCK);
    LOG_ALWAYS_FATAL_IF(result != 0, "Could not make wake read pipe non-blocking.  errno=%d",
            errno);

    result = fcntl(mWakeWritePipeFd, F_SETFL, O_NONBLOCK);
    LOG_ALWAYS_FATAL_IF(result != 0, "Could not make wake write pipe non-blocking.  errno=%d",
            errno);

    // Allocate the epoll instance and register the wake pipe.
    mEpollFd = epoll_create(EPOLL_SIZE_HINT);
    LOG_ALWAYS_FATAL_IF(mEpollFd < 0, "Could not create epoll instance.  errno=%d", errno);

    struct epoll_event eventItem;
    memset(& eventItem, 0, sizeof(epoll_event)); // zero out unused members of data field union
    eventItem.events = EPOLLIN;
    eventItem.data.fd = mWakeReadPipeFd;
    result = epoll_ctl(mEpollFd, EPOLL_CTL_ADD, mWakeReadPipeFd, & eventItem);
    LOG_ALWAYS_FATAL_IF(result != 0, "Could not add wake read pipe to epoll instance.  errno=%d",
            errno);
}

不同之處

int result = pipe(wakeFds);
    LOG_ALWAYS_FATAL_IF(result != 0, "Could not create wake pipe.  errno=%d", errno);
    mWakeReadPipeFd = wakeFds[0];
    mWakeWritePipeFd = wakeFds[1];
    .................
  struct epoll_event eventItem;
    memset(& eventItem, 0, sizeof(epoll_event)); // zero out unused members of data field union
    eventItem.events = EPOLLIN;
    eventItem.data.fd = mWakeReadPipeFd;
    result = epoll_ctl(mEpollFd, EPOLL_CTL_ADD, mWakeReadPipeFd, & eventItem);

epoll 監(jiān)控的是管道的讀端即mWakeReadPipeFd,而在Looper的函數(shù)中是向管道的mWakeWritePipeFd寫一個整形

void Looper::wake() {
#if DEBUG_POLL_AND_WAKE
    ALOGD("%p ~ wake", this);
#endif

    ssize_t nWrite;
    do {
        nWrite = write(mWakeWritePipeFd, "W", 1);
    } while (nWrite == -1 && errno == EINTR);

    if (nWrite != 1) {
        if (errno != EAGAIN) {
            ALOGW("Could not write wake signal, errno=%d", errno);
        }
    }
}

這樣也能實現(xiàn)喚醒功能

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末蒜胖,一起剝皮案震驚了整個濱河市消别,隨后出現(xiàn)的幾起案子抛蚤,更是在濱河造成了極大的恐慌,老刑警劉巖寻狂,帶你破解...
    沈念sama閱讀 217,826評論 6 506
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件岁经,死亡現(xiàn)場離奇詭異,居然都是意外死亡蛇券,警方通過查閱死者的電腦和手機缀壤,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 92,968評論 3 395
  • 文/潘曉璐 我一進店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來纠亚,“玉大人塘慕,你說我怎么就攤上這事〉侔” “怎么了图呢?”我有些...
    開封第一講書人閱讀 164,234評論 0 354
  • 文/不壞的土叔 我叫張陵,是天一觀的道長骗随。 經(jīng)常有香客問我蛤织,道長,這世上最難降的妖魔是什么鸿染? 我笑而不...
    開封第一講書人閱讀 58,562評論 1 293
  • 正文 為了忘掉前任指蚜,我火速辦了婚禮,結(jié)果婚禮上涨椒,老公的妹妹穿的比我還像新娘摊鸡。我一直安慰自己,他們只是感情好丢烘,可當我...
    茶點故事閱讀 67,611評論 6 392
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著些椒,像睡著了一般播瞳。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上免糕,一...
    開封第一講書人閱讀 51,482評論 1 302
  • 那天赢乓,我揣著相機與錄音,去河邊找鬼石窑。 笑死牌芋,一個胖子當著我的面吹牛,可吹牛的內(nèi)容都是我干的松逊。 我是一名探鬼主播躺屁,決...
    沈念sama閱讀 40,271評論 3 418
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼经宏!你這毒婦竟也來了犀暑?” 一聲冷哼從身側(cè)響起驯击,我...
    開封第一講書人閱讀 39,166評論 0 276
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎耐亏,沒想到半個月后徊都,有當?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 45,608評論 1 314
  • 正文 獨居荒郊野嶺守林人離奇死亡广辰,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 37,814評論 3 336
  • 正文 我和宋清朗相戀三年暇矫,在試婚紗的時候發(fā)現(xiàn)自己被綠了。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片择吊。...
    茶點故事閱讀 39,926評論 1 348
  • 序言:一個原本活蹦亂跳的男人離奇死亡李根,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出干发,到底是詐尸還是另有隱情朱巨,我是刑警寧澤,帶...
    沈念sama閱讀 35,644評論 5 346
  • 正文 年R本政府宣布枉长,位于F島的核電站冀续,受9級特大地震影響,放射性物質(zhì)發(fā)生泄漏必峰。R本人自食惡果不足惜洪唐,卻給世界環(huán)境...
    茶點故事閱讀 41,249評論 3 329
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望吼蚁。 院中可真熱鬧凭需,春花似錦、人聲如沸肝匆。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,866評論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽旗国。三九已至枯怖,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間能曾,已是汗流浹背度硝。 一陣腳步聲響...
    開封第一講書人閱讀 32,991評論 1 269
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留寿冕,地道東北人蕊程。 一個月前我還...
    沈念sama閱讀 48,063評論 3 370
  • 正文 我出身青樓,卻偏偏與公主長得像驼唱,于是被迫代替她去往敵國和親藻茂。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當晚...
    茶點故事閱讀 44,871評論 2 354

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