Android Input輸入事件處理流程分享(1)

努比亞技術(shù)團(tuán)隊(duì)原創(chuàng)內(nèi)容揽乱,轉(zhuǎn)載請(qǐng)務(wù)必注明出處鞋吉。

  • 傳遞流程
  • Java層事件傳遞流程
    • 傳遞過程
    • 詳細(xì)傳遞過程
      • Native傳遞事件到Java
      • InputEventReceiver分發(fā)事件
        • Java層事件入口
        • 對(duì)事件進(jìn)行兼容性處理
        • 轉(zhuǎn)變事件類型并加入隊(duì)列
        • 循環(huán)事件鏈并進(jìn)行分發(fā)
        • 將事件分發(fā)到InputStage
      • InputStage
        • 分發(fā)傳遞事件
        • 處理input事件
        • 處理key事件
      • DecorView處理事件
      • Window.Callback(Activity)分發(fā)事件
      • PhoneWindow中處理事件
      • DecorView繼續(xù)分發(fā)事件到View樹
      • 小結(jié)

安卓中輸入事件主要分為KeyEvent和MotionEvent兩種,本篇我主要介紹輸入事件是如何產(chǎn)生蒋荚,然后又通過什么方式向用戶層傳遞,過程中都發(fā)生了什么,文中所列出的代碼均是基于Android 11的源碼進(jìn)行介紹栗精。考慮到大家對(duì)上層事件的傳遞比較熟悉瞻鹏,所以悲立,此篇我采用從上至下倒序方式來介紹,主要介紹事件從native層到Java視圖之間的傳遞流程新博。

傳遞流程

在介紹之前先看一下安卓輸入事件傳遞的大致流程薪夕,本篇我會(huì)基于下圖中所列出的路線從上至下的方式進(jìn)行介紹:

Input事件傳遞

這里首先eventhub構(gòu)建的時(shí)候會(huì)遍歷整個(gè)/dev/input路徑下的fd,并將其添加到epoll中赫悄,同時(shí)還會(huì)監(jiān)聽此路徑下新的設(shè)備的創(chuàng)建和卸載原献。當(dāng)driver向特定描述符寫入事件后,會(huì)觸發(fā)喚醒epoll起來工作埂淮,這時(shí)候eventHub通過read方法從描述符中讀取原始事件姑隅,然后通過簡單封裝成rawEvent并傳遞給InputReader。
InputReader中的threadLoop中會(huì)調(diào)用eventHub的getEvents來獲取輸入事件倔撞,然后通過調(diào)用notifyxxx方法將事件傳遞到InputDispater并最終通過notifyxxx方法傳遞到上層讲仰。

Java層事件傳遞流程

Java層主要介紹事件從Native層如果向上層傳遞,并且事件是如何到達(dá)Activity痪蝇、Window以及ViewGroup的整個(gè)樹上的鄙陡。

傳遞過程

先來看一下Java層整個(gè)過程的調(diào)用時(shí)序圖:

Java層傳遞時(shí)序圖

詳細(xì)傳遞過程

Native傳遞事件到Java

在android_view_InputEventReceiver中的consumeEvents方法中會(huì)從InputConsumer中獲取input事件冕房,然后通過jni將事件向上層傳遞。

consumeEvents事件到Java
status_t NativeInputEventReceiver::consumeEvents(JNIEnv* env,
        bool consumeBatches, nsecs_t frameTime, bool* outConsumedBatch) {
    if (kDebugDispatchCycle) {
        ALOGD("channel '%s' ~ Consuming input events, consumeBatches=%s, frameTime=%" PRId64,
              getInputChannelName().c_str(), toString(consumeBatches), frameTime);
    }
    // 省略若干行
    // 這里通過InputConsumer的consume方法獲取到inputevent
    status_t status = mInputConsumer.consume(&mInputEventFactory,
            consumeBatches, frameTime, &seq, &inputEvent,
            &motionEventType, &touchMoveNum, &flag);
    // 省略若干行
    if (inputEventObj) {
        if (kDebugDispatchCycle) {
            ALOGD("channel '%s' ~ Dispatching input event.", getInputChannelName().c_str());
        }
        // 此處通過jni調(diào)用InputEventReceiver的dispatchInputEvent方法進(jìn)行事件的分發(fā)趁矾,
        // 從而將input事件傳遞到j(luò)ava層
        env->CallVoidMethod(receiverObj.get(),
                gInputEventReceiverClassInfo.dispatchInputEvent, seq, inputEventObj);
        if (env->ExceptionCheck()) {
            ALOGE("Exception dispatching input event.");
            skipCallbacks = true;
        }
        env->DeleteLocalRef(inputEventObj);
    } else {
        ALOGW("channel '%s' ~ Failed to obtain event object.",
                getInputChannelName().c_str());
        skipCallbacks = true;
    }
  
    if (skipCallbacks) {
        mInputConsumer.sendFinishedSignal(seq, false);
    }
}
  

InputEventReceiver分發(fā)事件

在InputEventReceiver中的dispatchInputEvent方法中會(huì)調(diào)用onInputEvent方法進(jìn)行事件的處理毒费,InputEventReceiver是抽象類,它的一個(gè)子類WindowInputEventReceiver是用來處理input事件的愈魏,WindowInputEventReceiver是frameworks/base/core/java/android/view/ViewRootImpl.java的內(nèi)部類觅玻,它復(fù)寫了onInputEvent方法,該方法中會(huì)調(diào)用enqueueInputEvent方法對(duì)事件進(jìn)行入隊(duì)培漏。

InputEventReceiver傳遞事件到InputStage
Java層事件入口
private void dispatchInputEvent(int seq, InputEvent event) {
    mSeqMap.put(event.getSequenceNumber(), seq);
    // 直接調(diào)用onInputEvent
    onInputEvent(event);
}
  
public void onInputEvent(InputEvent event) {
    // 直接調(diào)用finishInputEvent回收事件溪厘,所以此方法需要子類復(fù)寫
    finishInputEvent(event, false);
}

dispatchInputEvent方法是jni直接調(diào)用的方法,屬于Java層的入口方法牌柄,其中會(huì)直接調(diào)用onInputEvent方法畸悬,onInputEvent方法中直接調(diào)用了finishInputEvent回收事件,所以就需要子類去實(shí)現(xiàn)具體的分發(fā)邏輯珊佣。

對(duì)事件進(jìn)行兼容性處理

onInputEvent方法中首先調(diào)用InputCompatProcessor的processInputEventForCompatibility方法對(duì)事件進(jìn)行兼容性處理蹋宦,這個(gè)方法中會(huì)判斷應(yīng)用的targetSdkVersion如果小于M并且是motion事件則進(jìn)行兼容處理并返回,否則返回null咒锻;然后會(huì)調(diào)用enqueueInputEvent方法對(duì)事件進(jìn)行入隊(duì)冷冗。

@Override
public void onInputEvent(InputEvent event) {
    Trace.traceBegin(Trace.TRACE_TAG_VIEW, "processInputEventForCompatibility");
    List<InputEvent> processedEvents;
    try {
        // 這塊兒主要是對(duì)低版本進(jìn)行兼容性處理
        processedEvents =
            mInputCompatProcessor.processInputEventForCompatibility(event);
    } finally {
        Trace.traceEnd(Trace.TRACE_TAG_VIEW);
    }
    if (processedEvents != null) {
        // 安卓M及以下版本,在這里處理
        if (processedEvents.isEmpty()) {
            // InputEvent consumed by mInputCompatProcessor
            finishInputEvent(event, true);
        } else {
            for (int i = 0; i < processedEvents.size(); i++) {
                enqueueInputEvent(
                        processedEvents.get(i), this,
                        QueuedInputEvent.FLAG_MODIFIED_FOR_COMPATIBILITY, true);
            }
        }
    } else {
        // 這里對(duì)事件進(jìn)行入隊(duì)
        enqueueInputEvent(event, this, 0, true);
    }
}
  
public InputEventCompatProcessor(Context context) {
    mContext = context;
    // 獲取應(yīng)用的targetsdk版本
    mTargetSdkVersion = context.getApplicationInfo().targetSdkVersion;
    mProcessedEvents = new ArrayList<>();
}
  
public List<InputEvent> processInputEventForCompatibility(InputEvent e) {
    // 小于M并且是motion事件則進(jìn)行兼容處理
    if (mTargetSdkVersion < Build.VERSION_CODES.M && e instanceof MotionEvent) {
        mProcessedEvents.clear();
        MotionEvent motion = (MotionEvent) e;
        final int mask =
                MotionEvent.BUTTON_STYLUS_PRIMARY | MotionEvent.BUTTON_STYLUS_SECONDARY;
        final int buttonState = motion.getButtonState();
        final int compatButtonState = (buttonState & mask) >> 4;
        if (compatButtonState != 0) {
            motion.setButtonState(buttonState | compatButtonState);
        }
        mProcessedEvents.add(motion);
        return mProcessedEvents;
    }
    return null;
}
轉(zhuǎn)變事件類型并加入隊(duì)列

此方法主要是將InputEvent轉(zhuǎn)變?yōu)镼ueuedInputEvent并將其放到鏈表的末尾惑艇,然后調(diào)用doProcessInputEvents方法進(jìn)行處理

void enqueueInputEvent(InputEvent event,
        InputEventReceiver receiver, int flags, boolean processImmediately) {
    // 轉(zhuǎn)變?yōu)镼ueuedInputEvent
    QueuedInputEvent q = obtainQueuedInputEvent(event, receiver, flags);
  
    // 將事件插入到鏈表的末尾
    QueuedInputEvent last = mPendingInputEventTail;
    if (last == null) {
        mPendingInputEventHead = q;
        mPendingInputEventTail = q;
    } else {
        last.mNext = q;
        mPendingInputEventTail = q;
    }
    mPendingInputEventCount += 1;
    Trace.traceCounter(Trace.TRACE_TAG_INPUT, mPendingInputEventQueueLengthCounterName,
            mPendingInputEventCount);
  
    if (processImmediately) {
        // 調(diào)用doProcessInputEvents繼續(xù)處理
        doProcessInputEvents();
    } else {
        scheduleProcessInputEvents();
    }
}
循環(huán)事件鏈并進(jìn)行分發(fā)

此方法中會(huì)遍歷整個(gè)事件鏈表蒿辙,對(duì)每個(gè)事件調(diào)用deliverInputEvent方法進(jìn)行分發(fā)

void doProcessInputEvents() {
    // 遍歷整個(gè)鏈表,對(duì)事件進(jìn)行分發(fā)
    while (mPendingInputEventHead != null) {
        QueuedInputEvent q = mPendingInputEventHead;
        mPendingInputEventHead = q.mNext;
        if (mPendingInputEventHead == null) {
            mPendingInputEventTail = null;
        }
        q.mNext = null;
        // 省略若干行
        // 分發(fā)事件
        deliverInputEvent(q);
    }
}
將事件分發(fā)到InputStage

此方法中會(huì)根據(jù)flag獲取InputStage滨巴,然后調(diào)用InputStage的deliver方法分發(fā)事件思灌。這里的InputStage后面會(huì)介紹到,它主要是用來將事件的處理分成多個(gè)階段進(jìn)行恭取,詳細(xì)介紹見后文泰偿。

private void deliverInputEvent(QueuedInputEvent q) {
    // 省略若干行
    try {
        // 省略若干行
        InputStage stage;
        if (q.shouldSendToSynthesizer()) {
            // flag包含F(xiàn)LAG_UNHANDLED會(huì)走這里
            stage = mSyntheticInputStage;
        } else {
            // 是否跳過輸入法窗口進(jìn)行分發(fā)
            stage = q.shouldSkipIme() ? mFirstPostImeInputStage : mFirstInputStage;
        }
        // 省略若干行
        if (stage != null) {
            // 處理窗口焦點(diǎn)變更
            handleWindowFocusChanged();
            // 分發(fā)事件
            stage.deliver(q);
        } else {
            finishInputEvent(q);
        }
    } finally {
        Trace.traceEnd(Trace.TRACE_TAG_VIEW);
    }
}

InputStage

InputStage主要是用來將事件的處理分成若干個(gè)階段(stage)進(jìn)行,事件依次經(jīng)過每一個(gè)stage蜈垮,如果該事件沒有被處理(標(biāo)識(shí)為FLAG_FINISHED)萨赁,則該stage就會(huì)調(diào)用onProcess方法處理边器,然后調(diào)用forward執(zhí)行下一個(gè)stage的處理拧廊;如果該事件被標(biāo)識(shí)為處理則直接調(diào)用forward框沟,執(zhí)行下一個(gè)stage的處理牍氛,直到?jīng)]有下一個(gè)stage(也就是最后一個(gè)SyntheticInputStage)晨继。這里一共有7中stage,各個(gè)stage間串聯(lián)起來搬俊,形成一個(gè)鏈表紊扬,各個(gè)stage的處理過程大致如下圖所示:

InputStage

先來看下這些stage是如何串起來的蜒茄,所有的Stage都繼承于InputStage,而InputStage是抽象類餐屎,它的定義如下:

abstract class InputStage {
    private final InputStage mNext;
    /**
        * Creates an input stage.
        * @param next The next stage to which events should be forwarded.
        */
    public InputStage(InputStage next) {
        // 從構(gòu)成函數(shù)的定義能夠看到檀葛,傳入的next會(huì)賦值給當(dāng)前實(shí)例的next,
        // 因此腹缩,先插入的就會(huì)是最后一個(gè)節(jié)點(diǎn)(頭插法)屿聋,最終會(huì)形成一個(gè)鏈表
        mNext = next;
    }
}

在ViewRootImpl的setView方法中有以下代碼段:

// 如下創(chuàng)建出來的7個(gè)實(shí)例會(huì)串在一起形成一個(gè)鏈表,
// 鏈表的頭是最后創(chuàng)建出來的nativePreImeStage藏鹊,
// 鏈表的尾是首先構(gòu)造出來的mSyntheticInputStage
// Set up the input pipeline.
mSyntheticInputStage = new SyntheticInputStage();
InputStage viewPostImeStage = new ViewPostImeInputStage(mSyntheticInputStage);
InputStage nativePostImeStage = new NativePostImeInputStage(viewPostImeStage,
        "aq:native-post-ime:" + counterSuffix);
InputStage earlyPostImeStage = new EarlyPostImeInputStage(nativePostImeStage);
// 輸入法對(duì)應(yīng)的stage
InputStage imeStage = new ImeInputStage(earlyPostImeStage,
        "aq:ime:" + counterSuffix);
InputStage viewPreImeStage = new ViewPreImeInputStage(imeStage);
InputStage nativePreImeStage = new NativePreImeInputStage(viewPreImeStage,
        "aq:native-pre-ime:" + counterSuffix);
// 第一個(gè)處理事件的stage為NativePreImeInputStage
mFirstInputStage = nativePreImeStage;
mFirstPostImeInputStage = earlyPostImeStage;
分發(fā)傳遞事件

該方法中會(huì)通過flag判斷事件是否已經(jīng)處理润讥,若已經(jīng)處理則向前調(diào)用下一個(gè)stage處理事件(next的deliver方法),否則會(huì)調(diào)用onProcess來處理事件(此方法需要子類實(shí)現(xiàn))盘寡,然后會(huì)根據(jù)處理的結(jié)果判斷是否需要調(diào)用鏈表中的下一個(gè)stage來繼續(xù)處理楚殿。

public final void deliver(QueuedInputEvent q) {
    if ((q.mFlags & QueuedInputEvent.FLAG_FINISHED) != 0) {
        // 調(diào)用next的deliver方法繼續(xù)分發(fā)處理
        forward(q);
    } else if (shouldDropInputEvent(q)) {
        finish(q, false);
    } else {
        traceEvent(q, Trace.TRACE_TAG_VIEW);
        final int result;
        try {
            // 自身處理事件
            result = onProcess(q);
        } finally {
            Trace.traceEnd(Trace.TRACE_TAG_VIEW);
        }
        // 判斷是否需要繼續(xù)分發(fā)處理
        apply(q, result);
    }
}
處理input事件

這里我們以ViewPostImeInputStage為例(此stage會(huì)將事件傳遞到視圖層)介紹事件的分發(fā)過程,在onProcess方法中根據(jù)event的類型調(diào)用不同的方法進(jìn)行分發(fā)竿痰。

@Override
protected int onProcess(QueuedInputEvent q) {
    if (q.mEvent instanceof KeyEvent) {
        // 處理key事件
        return processKeyEvent(q);
    } else {
        final int source = q.mEvent.getSource();
        if ((source & InputDevice.SOURCE_CLASS_POINTER) != 0) {
            // 處理pointer事件
            return processPointerEvent(q);
        } else if ((source & InputDevice.SOURCE_CLASS_TRACKBALL) != 0) {
            // 處理軌跡球事件
            return processTrackballEvent(q);
        } else {
            // 處理一般的motion事件
            return processGenericMotionEvent(q);
        }
    }
}
處理key事件

此方法中會(huì)調(diào)用view的dispatchKeyEvent方法來將input事件分發(fā)到view樹上脆粥,然后按照view的事件分發(fā)機(jī)制繼續(xù)分發(fā)處理事件,需要注意的是這里的mView是decorview實(shí)例影涉,在ViewRootImpl的setView方法中被設(shè)置变隔。

private int processKeyEvent(QueuedInputEvent q) {
    final KeyEvent event = (KeyEvent)q.mEvent;
    // 省略若干行
    // 調(diào)用view的dispatchKeyEvent來分發(fā)事件,此處的mView是decorview
    if (mView.dispatchKeyEvent(event)) {
        return FINISH_HANDLED;
    }
    // 省略若干行
    // 繼續(xù)調(diào)用鏈表的下一個(gè)stage來處理
    return FORWARD;
}

DecorView處理事件

mView在ViewRootImpl的setView方法中被賦值蟹倾,被賦值的對(duì)象便是DecorView的實(shí)例弟胀,而在ViewPostImeInputStage的onPressess方法中會(huì)將key事件通過dispatchKeyEvent方法傳遞到DecorView中。

dispatchKeyEvent

此方法中先獲取Window.Callback對(duì)象喊式,然后調(diào)用其dispatchKeyEvent繼續(xù)處理孵户,若callback為null則調(diào)用父類同名方法來處理。最后岔留,還會(huì)回調(diào)window的onKeyDown和onKeyUp方法夏哭,需要注意的是Activity和Dialog都默認(rèn)實(shí)現(xiàn)了Window.Callback接口的方法,所以這里就會(huì)將事件傳遞到Activity或者Dialog里献联。

public boolean dispatchKeyEvent(KeyEvent event) {
    // 省略若干行
    if (!mWindow.isDestroyed()) {
        // window沒有銷毀竖配,如何存在Window.Callback,
        // 則調(diào)用callback的dispatchKeyEvent繼續(xù)處理
        // 否則調(diào)用父類的dispatchKeyEvent來處理
        final Window.Callback cb = mWindow.getCallback();
        final boolean handled = cb != null && mFeatureId < 0 ? cb.dispatchKeyEvent(event)
                : super.dispatchKeyEvent(event);
        if (handled) {
            return true;
        }
    }
  
    // 這里回調(diào)window的onKeyDown和onKeyUp方法
    return isDown ? mWindow.onKeyDown(mFeatureId, event.getKeyCode(), event)
            : mWindow.onKeyUp(mFeatureId, event.getKeyCode(), event);
}

Window.Callback(Activity)分發(fā)事件

這里以Activity來介紹事件的傳遞流程里逆,查看Activity的dispatchKeyEvent方法进胯。

dispatchKeyEvent
public boolean dispatchKeyEvent(KeyEvent event) {
    // 省略若干行
    // 調(diào)用Window的superDispatchKeyEvent方法繼續(xù)處理
    Window win = getWindow();
    if (win.superDispatchKeyEvent(event)) {
        return true;
    }
    View decor = mDecor;
    if (decor == null) decor = win.getDecorView();
    // 此處調(diào)用KeyEvent的dispatch,傳入的receiver為當(dāng)前實(shí)例原押,
    // 其內(nèi)部會(huì)根據(jù)event的action來調(diào)用當(dāng)前實(shí)例的onKeyDown和onKeyUp方法
    return event.dispatch(this, decor != null
            ? decor.getKeyDispatcherState() : null, this);
}

PhoneWindow中處理事件

Activity中g(shù)etWindow返回的其實(shí)是PhoneWindow的實(shí)例胁镐,看一下PhoneWindow的方法實(shí)現(xiàn)。

superDispatchKeyEvent

此方法中會(huì)直接調(diào)用mDecor的同名方法,這個(gè)mDecor是DecorView的實(shí)例盯漂。

public boolean superDispatchKeyEvent(KeyEvent event) {
    // 調(diào)用DecorView的同名方法
    return mDecor.superDispatchKeyEvent(event);
}

DecorView繼續(xù)分發(fā)事件到View樹

這里Activity通過調(diào)用DecorView的superDispatchKeyEvent方法颇玷,又將事件傳入到DecorView里了,這里和前面事件傳遞到DecorView的不同之處是前面?zhèn)鬟f到dispatchKeyEvent就缆,而這里是superDispatchKeyEvent帖渠。

superDispatchKeyEvent

此方法中會(huì)直接調(diào)用DecorView的父類的dispatchKeyEvent方法,如果事件沒有被處理掉的話竭宰,就會(huì)通過ViewRootImpl將其作為UnhandledEvent進(jìn)行處理空郊。

public boolean superDispatchKeyEvent(KeyEvent event) {
    // 省略若干行
    // 這里直接調(diào)用父類的dispatchKeyEvent方法,DecorView的父類是FrameLayout切揭,
    // 所以就分發(fā)到ViewGroup了渣淳,接下來就按照View樹的形式從根View向子View分發(fā)
    if (super.dispatchKeyEvent(event)) {
        return true;
    }
    // 如果沒有被消費(fèi)掉,則通過ViewRootImpl將其作為Unhandled繼續(xù)處理
    return (getViewRootImpl() != null) && getViewRootImpl().dispatchUnhandledKeyEvent(event);
}

小結(jié)

通過以上分析伴箩,我們便了解了input事件從Native層傳遞到Java層入愧,然后繼續(xù)傳遞到View樹上的整個(gè)過程。需要注意的是嗤谚,在各個(gè)傳遞環(huán)節(jié)棺蛛,如果調(diào)用方法返回了true,就代表它消費(fèi)掉了本次事件巩步,那么就不會(huì)繼續(xù)朝后傳遞旁赊。另外,對(duì)于ViewGroup來說椅野,存在onInterceptTouchEvent方法终畅,此方法用來攔截事件向子view分發(fā),如果返回了true竟闪,事件就會(huì)傳遞到當(dāng)前ViewGroup的onTouchEvent中而不在向子View傳遞离福;否則的話,會(huì)分發(fā)到當(dāng)前ViewGroup炼蛤,然后再傳遞到子View妖爷。由于View不能包含子View,所以View不存在攔截方法理朋。

InputStage傳遞事件到View樹
最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末絮识,一起剝皮案震驚了整個(gè)濱河市,隨后出現(xiàn)的幾起案子嗽上,更是在濱河造成了極大的恐慌次舌,老刑警劉巖,帶你破解...
    沈念sama閱讀 221,198評(píng)論 6 514
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件兽愤,死亡現(xiàn)場離奇詭異彼念,居然都是意外死亡挪圾,警方通過查閱死者的電腦和手機(jī),發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 94,334評(píng)論 3 398
  • 文/潘曉璐 我一進(jìn)店門国拇,熙熙樓的掌柜王于貴愁眉苦臉地迎上來洛史,“玉大人惯殊,你說我怎么就攤上這事酱吝。” “怎么了土思?”我有些...
    開封第一講書人閱讀 167,643評(píng)論 0 360
  • 文/不壞的土叔 我叫張陵务热,是天一觀的道長。 經(jīng)常有香客問我己儒,道長崎岂,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 59,495評(píng)論 1 296
  • 正文 為了忘掉前任闪湾,我火速辦了婚禮冲甘,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘途样。我一直安慰自己江醇,他們只是感情好,可當(dāng)我...
    茶點(diǎn)故事閱讀 68,502評(píng)論 6 397
  • 文/花漫 我一把揭開白布何暇。 她就那樣靜靜地躺著陶夜,像睡著了一般。 火紅的嫁衣襯著肌膚如雪裆站。 梳的紋絲不亂的頭發(fā)上条辟,一...
    開封第一講書人閱讀 52,156評(píng)論 1 308
  • 那天,我揣著相機(jī)與錄音宏胯,去河邊找鬼羽嫡。 笑死,一個(gè)胖子當(dāng)著我的面吹牛肩袍,可吹牛的內(nèi)容都是我干的厂僧。 我是一名探鬼主播,決...
    沈念sama閱讀 40,743評(píng)論 3 421
  • 文/蒼蘭香墨 我猛地睜開眼了牛,長吁一口氣:“原來是場噩夢(mèng)啊……” “哼颜屠!你這毒婦竟也來了?” 一聲冷哼從身側(cè)響起鹰祸,我...
    開封第一講書人閱讀 39,659評(píng)論 0 276
  • 序言:老撾萬榮一對(duì)情侶失蹤甫窟,失蹤者是張志新(化名)和其女友劉穎,沒想到半個(gè)月后蛙婴,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體粗井,經(jīng)...
    沈念sama閱讀 46,200評(píng)論 1 319
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 38,282評(píng)論 3 340
  • 正文 我和宋清朗相戀三年,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了浇衬。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片懒构。...
    茶點(diǎn)故事閱讀 40,424評(píng)論 1 352
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡,死狀恐怖耘擂,靈堂內(nèi)的尸體忽然破棺而出胆剧,到底是詐尸還是另有隱情,我是刑警寧澤醉冤,帶...
    沈念sama閱讀 36,107評(píng)論 5 349
  • 正文 年R本政府宣布秩霍,位于F島的核電站,受9級(jí)特大地震影響蚁阳,放射性物質(zhì)發(fā)生泄漏铃绒。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,789評(píng)論 3 333
  • 文/蒙蒙 一螺捐、第九天 我趴在偏房一處隱蔽的房頂上張望颠悬。 院中可真熱鬧,春花似錦定血、人聲如沸赔癌。這莊子的主人今日做“春日...
    開封第一講書人閱讀 32,264評(píng)論 0 23
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽届榄。三九已至,卻和暖如春倔喂,著一層夾襖步出監(jiān)牢的瞬間铝条,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 33,390評(píng)論 1 271
  • 我被黑心中介騙來泰國打工席噩, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留班缰,地道東北人。 一個(gè)月前我還...
    沈念sama閱讀 48,798評(píng)論 3 376
  • 正文 我出身青樓悼枢,卻偏偏與公主長得像埠忘,于是被迫代替她去往敵國和親。 傳聞我的和親對(duì)象是個(gè)殘疾皇子馒索,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 45,435評(píng)論 2 359

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