Android Input(四) -InputDispatcher分發(fā)事件

原創(chuàng)內(nèi)容骡送,轉(zhuǎn)載請(qǐng)注明出處,多謝配合。

上一篇分析了InputReader獲取事件過(guò)程搀绣,最終InputReader將input event放到InputDispatcher的mInboundQueue后喚醒InputDispacher。本篇就來(lái)分析下InputDispatcher的事件分發(fā)過(guò)程戳气。

一链患、InputDispatcher初始化
frameworks/native/services/inputflinger/InputManager.cpp

InputManager::InputManager(
       const sp<EventHubInterface>& eventHub,
       const sp<InputReaderPolicyInterface>& readerPolicy,
       const sp<InputDispatcherPolicyInterface>& dispatcherPolicy) {
   mDispatcher = new InputDispatcher(dispatcherPolicy);//創(chuàng)建InputDispatcher
...
}

frameworks/native/services/inputflinger/InputDispatcher.cpp

InputDispatcher::InputDispatcher(const sp<InputDispatcherPolicyInterface>& policy) ...  : {
   mLooper = new Looper(false);//這里InputDispatcher新建了一個(gè)Looper
   ...
}

InputDispatcherThread::InputDispatcherThread(const sp<InputDispatcherInterface>& dispatcher) :
       Thread(/*canCallJava*/ true), mDispatcher(dispatcher) {
}

bool InputDispatcherThread::threadLoop() {
   mDispatcher->dispatchOnce();//開(kāi)始處理分發(fā)
   return true;
}
二、InputDispatcher運(yùn)行
frameworks/native/services/inputflinger/InputDispatcher.cpp

void InputDispatcher::dispatchOnce() {
   nsecs_t nextWakeupTime = LONG_LONG_MAX;register_android_server_InputManager
   ...
       //優(yōu)先處理Command
       if (!haveCommandsLocked()) {
           //這里處理input event
           dispatchOnceInnerLocked(&nextWakeupTime);
       }
       if (runCommandsLockedInterruptible()) {
           // 處理完Command瓶您,立即喚醒loop來(lái)處理input event
           nextWakeupTime = LONG_LONG_MIN;
       }    
   ...
   nsecs_t currentTime = now();
   int timeoutMillis = toMillisecondTimeoutDelay(currentTime, nextWakeupTime);
   // 這里進(jìn)入epoll_wait麻捻,
   // InputReader可以用Looper::wake()喚醒InputDispacher
   // 窗口也可以通過(guò)回執(zhí)event喚醒Looper
   mLooper->pollOnce(timeoutMillis);
}

InputDispatcher處理的事件主要分兩種:一種是command命令(Command是mPolicy處理的具體事務(wù),這個(gè)mPolicy就是NativeInputManager呀袱,最終對(duì)應(yīng)的上層是InputManagerService)贸毕,一種是input event。每次dispatchOnce夜赵,會(huì)優(yōu)先執(zhí)行前者明棍。

這里Command一些列回調(diào)命令保存在mCommandQueue中,主要包括:
doPokeUserActivityLockedInterruptible
doNotifyANRLockedInterruptible
doInterceptKeyBeforeDispatchingLockedInterruptible
doDispatchCycleFinishedLockedInterruptible
doNotifyInputChannelBrokenLockedInterruptible
doNotifyConfigurationChangedInterruptible

再看dispatchOnceInnerLocked處理input event部分:

void InputDispatcher::dispatchOnceInnerLocked(nsecs_t* nextWakeupTime) {
    nsecs_t currentTime = now(); 
 ...
   //當(dāng)事件分發(fā)的時(shí)間點(diǎn)距離該事件加入mInboundQueue的時(shí)間超過(guò)500ms,則認(rèn)為app切換過(guò)期,即  
//isAppSwitchDue=true;
   bool isAppSwitchDue = mAppSwitchDueTime <= currentTime;
  ...
           if (!mPendingEvent) {
                return;
            }
        } else {
           //事件是從InboundQueue頭部取的
           mPendingEvent = mInboundQueue.dequeueAtHead();
            traceInboundQueueLengthLocked();
        }
        // Poke user activity for this event.
       if (mPendingEvent->policyFlags & POLICY_FLAG_PASS_TO_USER) {
            pokeUserActivityLocked(mPendingEvent);
        }
        // Get ready to dispatch the event.
       resetANRTimeoutsLocked(); //重置ANR 超時(shí)時(shí)間
    }
    // Now we have an event to dispatch.
   // All events are eventually dequeued and processed this way, even if we intend to drop them.
   ALOG_ASSERT(mPendingEvent != NULL);
    bool done = false;
    DropReason dropReason = DROP_REASON_NOT_DROPPED;
    if (!(mPendingEvent->policyFlags & POLICY_FLAG_PASS_TO_USER)) {
        dropReason = DROP_REASON_POLICY;
    } else if (!mDispatchEnabled) {
        dropReason = DROP_REASON_DISABLED;
    }
    if (mNextUnblockedEvent == mPendingEvent) {
        mNextUnblockedEvent = NULL;
    }
    switch (mPendingEvent->type) {
    …
   //這里有很多類型的EventEntry type 重點(diǎn)看看key
    case EventEntry::TYPE_KEY: {
        KeyEntry* typedEntry = static_cast<KeyEntry*>(mPendingEvent);
        if (isAppSwitchDue) {
            if (isAppSwitchKeyEventLocked(typedEntry)) {
                resetPendingAppSwitchLocked(true);
                isAppSwitchDue = false;
            } else if (dropReason == DROP_REASON_NOT_DROPPED) {
                dropReason = DROP_REASON_APP_SWITCH;
            }
        }
        if (dropReason == DROP_REASON_NOT_DROPPED
                && isStaleEventLocked(currentTime, typedEntry)) {
            dropReason = DROP_REASON_STALE;
        }

        if (dropReason == DROP_REASON_NOT_DROPPED && mNextUnblockedEvent) {
            dropReason = DROP_REASON_BLOCKED;
        }

        //分發(fā)事件處理
        done = dispatchKeyLocked(currentTime, typedEntry, &dropReason, nextWakeupTime);
        break;
    }
    ...
    }
    //分發(fā)操作完成
    if (done) {
        if (dropReason != DROP_REASON_NOT_DROPPED) {
            dropInboundEventLocked(mPendingEvent, dropReason);//丟棄事件
        }
        mLastDropReason = dropReason;
        releasePendingEventLocked();//釋放當(dāng)前正在處理的事件
        *nextWakeupTime = LONG_LONG_MIN;  // force next poll to wake up immediately
   }
}

這個(gè)過(guò)程簡(jiǎn)單看就是從mInboundQueue頭部取出事件交給dispatchKeyLocked執(zhí)行分發(fā)處理寇僧,并重置ANR時(shí)間摊腋,以及執(zhí)行完之后根據(jù)done來(lái)做一些收尾操作。

接下來(lái)看看dispatchKeyLocked:

bool InputDispatcher::dispatchKeyLocked(nsecs_t currentTime, KeyEntry* entry,
        DropReason* dropReason, nsecs_t* nextWakeupTime) {
    ...
    //尋找焦點(diǎn)窗口inputTargets
    int32_t injectionResult = findFocusedWindowTargetsLocked(currentTime,
            entry, inputTargets, nextWakeupTime);
    if (injectionResult == INPUT_EVENT_INJECTION_PENDING) {
        return false;
    }
    setInjectionResultLocked(entry, injectionResult);
    if (injectionResult != INPUT_EVENT_INJECTION_SUCCEEDED) {
        return true;
    }
    addMonitoringTargetsLocked(inputTargets);
    //只有injectionResult為true才會(huì)繼續(xù)往下執(zhí)行分發(fā)操作
   dispatchEventLocked(currentTime, entry, inputTargets);
    return true;
}

這個(gè)方法核心內(nèi)容是執(zhí)行findFocusedWindowTargetsLocked尋找焦點(diǎn)窗口inputTargets嘁傀,但是在此之前會(huì)有若干判斷有可能直接return 兴蒸。

int32_t InputDispatcher::findFocusedWindowTargetsLocked(nsecs_t currentTime,
        const EventEntry* entry, Vector<InputTarget>& inputTargets, nsecs_t* nextWakeupTime) {
    int32_t injectionResult;
    String8 reason;
  ...
    // Check permissions.
   if (! checkInjectionPermission(mFocusedWindowHandle, entry->injectionState)) {
        injectionResult = INPUT_EVENT_INJECTION_PERMISSION_DENIED;
        goto Failed;
    }
    // 檢測(cè)窗口是否為更多的輸入操作而準(zhǔn)備就緒
   reason = checkWindowReadyForMoreInputLocked(currentTime,
            mFocusedWindowHandle, entry, "focused");
    if (!reason.isEmpty()) { //如果滿足,handleTargetsNotReadyLocked會(huì)觸發(fā)anr判斷
        injectionResult = handleTargetsNotReadyLocked(currentTime, entry,
                mFocusedApplicationHandle, mFocusedWindowHandle, nextWakeupTime, reason.string());
        goto Unresponsive;
    }
    // Success!  Output targets.
   injectionResult = INPUT_EVENT_INJECTION_SUCCEEDED;
    //添加目標(biāo)窗口
    addWindowTargetLocked(mFocusedWindowHandle,
            InputTarget::FLAG_FOREGROUND | InputTarget::FLAG_DISPATCH_AS_IS, BitSet32(0),
            inputTargets);
    // Done.
Failed:
Unresponsive:
    nsecs_t timeSpentWaitingForApplication = getTimeSpentWaitingForApplicationLocked(currentTime);
    updateDispatchStatisticsLocked(currentTime, entry,
            injectionResult, timeSpentWaitingForApplication);
#if DEBUG_FOCUS
    ALOGD("findFocusedWindow finished: injectionResult=%d, "
           "timeSpentWaitingForApplication=%0.1fms",
            injectionResult, timeSpentWaitingForApplication / 1000000.0);
#endif
   return injectionResult;
}

這里主要就是添加目標(biāo)窗口细办,與目前窗口建立InputChannel連接

void InputDispatcher::addWindowTargetLocked(const sp<InputWindowHandle>& windowHandle,
        int32_t targetFlags, BitSet32 pointerIds, Vector<InputTarget>& inputTargets) {
    inputTargets.push();
    const InputWindowInfo* windowInfo = windowHandle->getInfo();
    InputTarget& target = inputTargets.editTop();
    target.inputChannel = windowInfo->inputChannel;
    target.flags = targetFlags;
    target.xOffset = - windowInfo->frameLeft;
    target.yOffset = - windowInfo->frameTop;
    target.scaleFactor = windowInfo->scaleFactor;
    target.pointerIds = pointerIds;
}

將當(dāng)前聚焦窗口mFocusedWindowHandle的inputChannel傳遞到inputTargets类咧。

void InputDispatcher::dispatchEventLocked(nsecs_t currentTime,
        EventEntry* eventEntry, const Vector<InputTarget>& inputTargets) {
#if DEBUG_DISPATCH_CYCLE
    ALOGD("dispatchEventToCurrentInputTargets");
#endif
   ALOG_ASSERT(eventEntry->dispatchInProgress); // should already have been set to true
   //調(diào)用到Java層的PowerManagerService.java中的userActivityFromNative()方法. 這也是PMS中唯一的native call方法
   pokeUserActivityLocked(eventEntry);
    for (size_t i = 0; i < inputTargets.size(); i++) {
        const InputTarget& inputTarget = inputTargets.itemAt(i);
       //根據(jù)inputChannel的fd從mConnectionsByFd隊(duì)列中查詢目標(biāo)connection.
        ssize_t connectionIndex = getConnectionIndexLocked(inputTarget.inputChannel);
        if (connectionIndex >= 0) {
            sp<Connection> connection = mConnectionsByFd.valueAt(connectionIndex);
            //找到目標(biāo)連接
            prepareDispatchCycleLocked(currentTime, connection, eventEntry, &inputTarget);
        } else {
#if DEBUG_FOCUS
            ALOGD("Dropping event delivery to target with channel '%s' because it "
                   "is no longer registered with the input dispatcher.",
                    inputTarget.inputChannel->getName().string());
#endif
       }
    }
}

該方法主要功能是將eventEntry發(fā)送到目標(biāo)inputTargets。

void InputDispatcher::prepareDispatchCycleLocked(nsecs_t currentTime,
       const sp<Connection>& connection, EventEntry* eventEntry, const InputTarget* inputTarget) {
 ...
   enqueueDispatchEntriesLocked(currentTime, connection, eventEntry, inputTarget);
}

void InputDispatcher::enqueueDispatchEntriesLocked(nsecs_t currentTime, const sp<Connection>& connection, EventEntry* eventEntry, const InputTarget* inputTarget) {
   bool wasEmpty = connection->outboundQueue.isEmpty();
   ...
   enqueueDispatchEntryLocked(connection, eventEntry, inputTarget, InputTarget::FLAG_DISPATCH_AS_IS);
   ...
   if (wasEmpty && !connection->outboundQueue.isEmpty()) {
       startDispatchCycleLocked(currentTime, connection);
   }
}

void InputDispatcher::enqueueDispatchEntryLocked(const sp<Connection>& connection, EventEntry* eventEntry, const InputTarget* inputTarget,  int32_t dispatchMode) {
   int32_t inputTargetFlags = inputTarget->flags;
   if (!(inputTargetFlags & dispatchMode)) {
       return;
   }
   //這里又轉(zhuǎn)了一次數(shù)據(jù)結(jié)構(gòu)蟹腾,生成新的事件, 加入connection的outbound隊(duì)列痕惋。
   DispatchEntry* dispatchEntry = new DispatchEntry(eventEntry, inputTargetFlags, inputTarget->xOffset, inputTarget->yOffset, inputTarget->scaleFactor);
   switch (eventEntry->type) {
   case EventEntry::TYPE_KEY: {
       KeyEntry* keyEntry = static_cast<KeyEntry*>(eventEntry);
       dispatchEntry->resolvedAction = keyEntry->action;
       dispatchEntry->resolvedFlags = keyEntry->flags;
       break;
   }

   //添加到outboundQueue隊(duì)尾
   connection->outboundQueue.enqueueAtTail(dispatchEntry);
}

執(zhí)行到這里,其實(shí)等于由做了一次搬運(yùn)的工作,將InputDispatcher中mInboundQueue中的eventEntry事件取出后, 找到目標(biāo)window后,將eventEntry封裝dispatchEntry加入到connection的outbound隊(duì)列。

void InputDispatcher::startDispatchCycleLocked(nsecs_t currentTime, const sp<Connection>& connection) {
   while (connection->status == Connection::STATUS_NORMAL && !connection->outboundQueue.isEmpty()) {
       DispatchEntry* dispatchEntry = connection->outboundQueue.head;
       dispatchEntry->deliveryTime = currentTime;
       // Publish the event.
       status_t status;
       EventEntry* eventEntry = dispatchEntry->eventEntry;
       switch (eventEntry->type) {
       case EventEntry::TYPE_KEY: {
           KeyEntry* keyEntry = static_cast<KeyEntry*>(eventEntry);
           status = connection->inputPublisher.publishKeyEvent(dispatchEntry->seq,
                   keyEntry->deviceId, keyEntry->source,
                   dispatchEntry->resolvedAction, dispatchEntry->resolvedFlags,
                   keyEntry->keyCode, keyEntry->scanCode,
                   keyEntry->metaState, keyEntry->repeatCount, keyEntry->downTime,
                   keyEntry->eventTime);
           break;
       }
       ...
       }
       // Re-enqueue the event on the wait queue.
       connection->outboundQueue.dequeue(dispatchEntry);
       connection->waitQueue.enqueueAtTail(dispatchEntry);
    }

通過(guò)InputPublisher將DispatchEntry發(fā)送給窗口娃殖,再將DispatchEntry從outboundQueue移到waitQueue里值戳。該通信過(guò)程是異步的,當(dāng)窗口處理完事件后會(huì)通過(guò)handleReceiveCallback()回調(diào)函數(shù)通知InputDispatcher炉爆。

status_t InputPublisher::publishKeyEvent(...) {
   if (!seq) {
       return BAD_VALUE;
   }
   InputMessage msg;
   msg.header.type = InputMessage::TYPE_KEY;
   msg.body.key.seq = seq;
   msg.body.key.deviceId = deviceId;
   msg.body.key.source = source;
   msg.body.key.action = action;
   msg.body.key.flags = flags;
   msg.body.key.keyCode = keyCode;
   msg.body.key.scanCode = scanCode;
   msg.body.key.metaState = metaState;
   msg.body.key.repeatCount = repeatCount;
   msg.body.key.downTime = downTime;
   msg.body.key.eventTime = eventTime;
   //通過(guò)InputChannel來(lái)發(fā)送消息
   return mChannel->sendMessage(&msg);
}

publishKeyEvent調(diào)用InputChanel的SendMessage()堕虹,SendMessage()再動(dòng)用socket的send()函數(shù)卧晓,將打包好的Message發(fā)送給窗口。

最后看看handleReceiveCallback回調(diào):

frameworks/native/services/inputflinger/InputDispatcher.cpp
int InputDispatcher::handleReceiveCallback(int fd, int events, void* data) {
    InputDispatcher* d = static_cast<InputDispatcher*>(data); 
    ssize_t connectionIndex = d->mConnectionsByFd.indexOfKey(fd); 
    sp<Connection> connection = d->mConnectionsByFd.valueAt(connectionIndex);
    if (!(events & (ALOOPER_EVENT_ERROR | ALOOPER_EVENT_HANGUP))) {
        nsecs_t currentTime = now();
        bool gotOne = false;
        status_t status;
        for (;;) {
            uint32_t seq;
            bool handled;
            //接收窗口處理完成的消息
            status = connection->inputPublisher.receiveFinishedSignal(&seq, &handled);
            if (status) { // 讀完socket即返回WOULD_BLOCK
                break;
            }
            // 這里post一個(gè)command
            d->finishDispatchCycleLocked(currentTime, connection, seq, handled);
            gotOne = true;
        }
        if (gotOne) {
            // 這里執(zhí)行command的handle
            d->runCommandsLockedInterruptible();
            if (status == WOULD_BLOCK) {  // 正常流程走這里
                return 1;
            }
        }
    }
    // 這里是socket鏈接出現(xiàn)異常的情況
    d->unregisterInputChannelLocked(connection->inputChannel, notify);
    return 0; // remove the callback
}

再看看post command

frameworks/native/services/inputflinger/InputDispatcher.cpp

void InputDispatcher::finishDispatchCycleLocked(nsecs_t currentTime, const sp<Connection>& connection, uint32_t seq, bool handled) {
   ...
   onDispatchCycleFinishedLocked(currentTime, connection, seq, handled);
}
void InputDispatcher::onDispatchCycleFinishedLocked(nsecs_t currentTime, const sp<Connection>& connection, uint32_t seq, bool handled) {
   CommandEntry* commandEntry = postCommandLocked( & InputDispatcher::doDispatchCycleFinishedLockedInterruptible);
   commandEntry->connection = connection;
   commandEntry->eventTime = currentTime;
   commandEntry->seq = seq;
   commandEntry->handled = handled;
}

frameworks/native/services/inputflinger/InputDispatcher.cpp

void InputDispatcher::doDispatchCycleFinishedLockedInterruptible(CommandEntry* commandEntry) {
   sp<Connection> connection = commandEntry->connection;
   nsecs_t finishTime = commandEntry->eventTime;
   uint32_t seq = commandEntry->seq;
   bool handled = commandEntry->handled;
  // Handle post-event policy actions.
   DispatchEntry* dispatchEntry = connection->findWaitQueueEntry(seq);
   if (dispatchEntry) {
       nsecs_t eventDuration = finishTime - dispatchEntry->deliveryTime;
      // 如果一個(gè)dispatch周期超過(guò)2秒赴捞,將打印警告信息
       if (eventDuration > SLOW_EVENT_PROCESSING_WARNING_TIMEOUT /* 2sec */) {
           String8 msg;
           msg.appendFormat("Window '%s' spent %0.1fms processing the last input event: ", connection->getWindowName(), eventDuration * 0.000001f);
           dispatchEntry->eventEntry->appendDescription(msg);
           ALOGI("%s", msg.string());
       }
       bool restartEvent;
       if (dispatchEntry->eventEntry->type == EventEntry::TYPE_KEY) {
           KeyEntry* keyEntry = static_cast<KeyEntry*>(dispatchEntry->eventEntry);
           // 主要是對(duì)unhandle key的policy逼裆,這里不做分析
           restartEvent = afterKeyEventLockedInterruptible(connection, dispatchEntry, keyEntry, handled);
       } else if (dispatchEntry->eventEntry->type == EventEntry::TYPE_MOTION) {
           MotionEntry* motionEntry = static_cast<MotionEntry*>(dispatchEntry->eventEntry);
           restartEvent = afterMotionEventLockedInterruptible(connection, dispatchEntry, motionEntry, handled);
       } else {
           restartEvent = false;
       }
       if (dispatchEntry == connection->findWaitQueueEntry(seq)) {
           // 從waitQueue中將當(dāng)前entry移除
           connection->waitQueue.dequeue(dispatchEntry);
           traceWaitQueueLengthLocked(connection);
           if (restartEvent && connection->status == Connection::STATUS_NORMAL) {
               connection->outboundQueue.enqueueAtHead(dispatchEntry);
               traceOutboundQueueLengthLocked(connection);
           } else {
              // 釋放內(nèi)存
               releaseDispatchEntryLocked(dispatchEntry);
           }
       }
      // 如果當(dāng)前connection的outBoundQueque里還有Event,則繼續(xù)下一輪的Dispatch周期
       startDispatchCycleLocked(now(), connection);
   }
}

從waitQueue中將當(dāng)前DispatchEntry移除赦政,如果當(dāng)前connection的outBoundQueque里還有EventEntry胜宇,則繼續(xù)下一輪的Dispatch周期。

最后放上整體流程圖和時(shí)序圖:

整體流程圖
時(shí)序圖

簡(jiǎn)單總結(jié)下InputDispatcher分發(fā)事件流程:

1)InputReader喚醒InputDispacher執(zhí)行分發(fā)處理恢着。

InputReader主要是將EventHub中的input_event轉(zhuǎn)換成具體的EventEntry,并添加到InputDispatcher的mInboundQueue里,然后喚醒InputDispacher線程桐愉。InputDispacher線程有自己的Looper,被喚醒后會(huì)執(zhí)行dispatchOnce方法掰派,InputDispatcher處理的事件主要分兩種:一種是command命令(Command是mPolicy處理的具體事務(wù)从诲,這個(gè)mPolicy就是NativeInputManager,最終對(duì)應(yīng)的上層是InputManagerService)靡羡,一種是input event系洛。每次dispatchOnce,會(huì)優(yōu)先執(zhí)行前者略步。

2)InboundQueue隊(duì)頭取出事件描扯,匹配焦點(diǎn)窗口,傳遞eventEntry纳像。

從mInboundQueue隊(duì)列頭部取出一個(gè)事件荆烈,尋找與之匹配的焦點(diǎn)窗口的inputTargets拯勉,這里先通過(guò)InputWindowHandle找到InputWindowInfo竟趾,再找到InputChannel,最終找到Connection宫峦,每個(gè)焦點(diǎn)窗口在InputDispacher里都有一個(gè)對(duì)應(yīng)的Connection岔帽,通過(guò)這個(gè)Connection可以跟InputDispacher通信。然后會(huì)將eventEntry轉(zhuǎn)為DispatchEntry放入Connection的outboundQueue导绷。通過(guò)InputPublisher通過(guò) InputChannel::sendMessage將DispatchEntry發(fā)送給窗口犀勒,再將dispatchEntry從outboundQueue移到waitQueue里。該通信過(guò)程是異步的妥曲,也就是說(shuō)當(dāng)InputDispatcher將Input事件發(fā)送給目標(biāo)窗口后會(huì)立即返回贾费,并不會(huì)等待窗口的處理結(jié)果。

3)窗口處理完又將處理結(jié)果返回給InputDispatcher

當(dāng)窗口處理完事件后檐盟,會(huì)通過(guò)handleReceiveCallback()回調(diào)函數(shù)通知InputDispatcher褂萧。回調(diào)主要是post一個(gè)command,從waitQueue中將當(dāng)前DispatchEntry移除葵萎,如果當(dāng)前connection的outBoundQueque里還有DispatchEntry导犹,則繼續(xù)下一輪的Dispatch周期唱凯。

到這里InputDispatcher分發(fā)事件過(guò)程就介紹完了。

下一篇文章:
Android Input(五)-InputChannel通信

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
禁止轉(zhuǎn)載谎痢,如需轉(zhuǎn)載請(qǐng)通過(guò)簡(jiǎn)信或評(píng)論聯(lián)系作者磕昼。
  • 序言:七十年代末,一起剝皮案震驚了整個(gè)濱河市节猿,隨后出現(xiàn)的幾起案子票从,更是在濱河造成了極大的恐慌,老刑警劉巖沐批,帶你破解...
    沈念sama閱讀 216,372評(píng)論 6 498
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件纫骑,死亡現(xiàn)場(chǎng)離奇詭異,居然都是意外死亡九孩,警方通過(guò)查閱死者的電腦和手機(jī)先馆,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 92,368評(píng)論 3 392
  • 文/潘曉璐 我一進(jìn)店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來(lái)躺彬,“玉大人煤墙,你說(shuō)我怎么就攤上這事∠苡担” “怎么了仿野?”我有些...
    開(kāi)封第一講書(shū)人閱讀 162,415評(píng)論 0 353
  • 文/不壞的土叔 我叫張陵,是天一觀的道長(zhǎng)她君。 經(jīng)常有香客問(wèn)我脚作,道長(zhǎng),這世上最難降的妖魔是什么缔刹? 我笑而不...
    開(kāi)封第一講書(shū)人閱讀 58,157評(píng)論 1 292
  • 正文 為了忘掉前任球涛,我火速辦了婚禮,結(jié)果婚禮上校镐,老公的妹妹穿的比我還像新娘亿扁。我一直安慰自己,他們只是感情好鸟廓,可當(dāng)我...
    茶點(diǎn)故事閱讀 67,171評(píng)論 6 388
  • 文/花漫 我一把揭開(kāi)白布从祝。 她就那樣靜靜地躺著,像睡著了一般引谜。 火紅的嫁衣襯著肌膚如雪牍陌。 梳的紋絲不亂的頭發(fā)上,一...
    開(kāi)封第一講書(shū)人閱讀 51,125評(píng)論 1 297
  • 那天员咽,我揣著相機(jī)與錄音毒涧,去河邊找鬼。 笑死骏融,一個(gè)胖子當(dāng)著我的面吹牛链嘀,可吹牛的內(nèi)容都是我干的萌狂。 我是一名探鬼主播,決...
    沈念sama閱讀 40,028評(píng)論 3 417
  • 文/蒼蘭香墨 我猛地睜開(kāi)眼怀泊,長(zhǎng)吁一口氣:“原來(lái)是場(chǎng)噩夢(mèng)啊……” “哼茫藏!你這毒婦竟也來(lái)了?” 一聲冷哼從身側(cè)響起霹琼,我...
    開(kāi)封第一講書(shū)人閱讀 38,887評(píng)論 0 274
  • 序言:老撾萬(wàn)榮一對(duì)情侶失蹤务傲,失蹤者是張志新(化名)和其女友劉穎,沒(méi)想到半個(gè)月后枣申,有當(dāng)?shù)厝嗽跇?shù)林里發(fā)現(xiàn)了一具尸體售葡,經(jīng)...
    沈念sama閱讀 45,310評(píng)論 1 310
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 37,533評(píng)論 2 332
  • 正文 我和宋清朗相戀三年忠藤,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了挟伙。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點(diǎn)故事閱讀 39,690評(píng)論 1 348
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡模孩,死狀恐怖尖阔,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情榨咐,我是刑警寧澤介却,帶...
    沈念sama閱讀 35,411評(píng)論 5 343
  • 正文 年R本政府宣布,位于F島的核電站块茁,受9級(jí)特大地震影響齿坷,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜数焊,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,004評(píng)論 3 325
  • 文/蒙蒙 一永淌、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧昌跌,春花似錦仰禀、人聲如沸照雁。這莊子的主人今日做“春日...
    開(kāi)封第一講書(shū)人閱讀 31,659評(píng)論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)饺蚊。三九已至萍诱,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間污呼,已是汗流浹背裕坊。 一陣腳步聲響...
    開(kāi)封第一講書(shū)人閱讀 32,812評(píng)論 1 268
  • 我被黑心中介騙來(lái)泰國(guó)打工, 沒(méi)想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留燕酷,地道東北人籍凝。 一個(gè)月前我還...
    沈念sama閱讀 47,693評(píng)論 2 368
  • 正文 我出身青樓周瞎,卻偏偏與公主長(zhǎng)得像,于是被迫代替她去往敵國(guó)和親饵蒂。 傳聞我的和親對(duì)象是個(gè)殘疾皇子声诸,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 44,577評(píng)論 2 353

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