Android事件分發(fā)機制詳解

系統(tǒng)機制分析

Android 系統(tǒng)是由事件驅(qū)動的撑刺,而 input 是最常見的事件之一,用戶的點擊挠铲、滑動拂苹、長按等操作瓢棒,都屬于 input 事件驅(qū)動脯宿,其中的核心就是 InputReader 和 InputDispatcher镐侯。InputReader 和 InputDispatcher 是跑在 SystemServer進程中的兩個 native 循環(huán)線程翠语,負責讀取和分發(fā) Input 事件点骑。整個處理過程大致流程如下:

InputReader負責從EventHub里面把Input事件讀取出來黑滴,然后交給 InputDispatcher 進行事件分發(fā)袁辈;
InputDispatcher在拿到 InputReader獲取的事件之后晚缩,對事件進行包裝后,尋找并分發(fā)到目標窗口;
InboundQueue隊列(“iq”)中放著InputDispatcher從InputReader中拿到的input事件鸣皂;
OutboundQueue(“oq”)隊列里面放的是即將要被派發(fā)給各個目標窗口App的事件齐邦;
WaitQueue隊列里面記錄的是已經(jīng)派發(fā)給 App(“wq”)措拇,但是 App還在處理沒有返回處理成功的事件;
PendingInputEventQueue隊列(“aq”)中記錄的是應(yīng)用需要處理的Input事件券犁,這里可以看到input事件已經(jīng)傳遞到了應(yīng)用進程;
deliverInputEvent 標識 AppUI Thread 被 Input 事件喚醒稚新;
InputResponse 標識 Input 事件區(qū)域跪腹,這里可以看到一個 Input_Down 事件 + 若干個 Input_Move 事件 + 一個 Input_Up 事件的處理階段都被算到了這里褂删;
App 響應(yīng)處理Input 事件,內(nèi)部會在其界面View樹中傳遞處理冲茸。


Android input事件驅(qū)動.jpg

目錄

Android事件分發(fā)機制.png

基礎(chǔ)

1.1 事件分發(fā)的對象
當用戶觸摸屏幕時(View或ViewGroup派生的控件)屯阀,將產(chǎn)生點擊事件(Touch事件)。
Touch事件相關(guān)細節(jié)(發(fā)生觸摸的位置轴术、時間难衰、歷史記錄、手勢動作等)被封裝成MotionEvent對象

主要發(fā)生的Touch事件有如下四種:

MotionEvent.ACTION_DOWN:按下View(所有事件的開始)
MotionEvent.ACTION_MOVE:滑動View
MotionEvent.ACTION_CANCEL:非人為原因結(jié)束本次事件
MotionEvent.ACTION_UP:抬起View(與DOWN對應(yīng))
事件列:從手指接觸屏幕至手指離開屏幕盖袭,這個過程產(chǎn)生的一系列事件 任何事件列都是以DOWN事件開始醇蝴,UP事件結(jié)束霉涨,中間有無數(shù)的MOVE事件往枷,如下圖:

Android事件流.png

1.2 事件分發(fā)的本質(zhì)
當一個點擊事件發(fā)生后,系統(tǒng)需要將這個事件傳遞給一個具體的View去處理导而。這個事件傳遞的過程就是分發(fā)過程洼滚。

1.3 事件在哪些對象之間進行傳遞
主要涉及Activity享幽、ViewGroup奔坟、View

1.4 事件分發(fā)過程由哪些方法協(xié)作完成
主要有三個方法dispatchTouchEvent() 鸯隅、onInterceptTouchEvent()和onTouchEvent()

詳細流程

通過一張圖理解

Android事件分發(fā)詳細流程.png

1溶推、 Activity
Activity要做的事情是轉(zhuǎn)發(fā)到下層,都沒有消費的情況下,最后由Activity來收尾兜底

2、 ViewGroup
1、是否在容器層面攔截
2、該事件是否要分發(fā)位谋,取消事件不需要消費
3赊淑、傳遞子View消費之前還要看是不是有效的组哩,是點在了哪一個View上
4罐栈、所以需要遍歷所有子View確定是否存在

源碼:

public boolean dispatchTouchEvent(MotionEvent ev) {
        //驗證事件是否連續(xù)
        if (mInputEventConsistencyVerifier != null) {
            mInputEventConsistencyVerifier.onTouchEvent(ev, 1);
        }
        //這個變量用于記錄事件是否被處理完
        boolean handled = false;
        //過濾掉一些不合法的事件:當前的View的窗口被遮擋了。
        if (onFilterTouchEventForSecurity(ev)) {
            //如果事件發(fā)生的View在的窗口,沒有被遮擋
            final int action = ev.getAction();
            //重置前面為0 空盼,只留下后八位篱瞎,用于判斷相等時候校哎,可以提高性能。
            final int actionMasked = action & MotionEvent.ACTION_MASK;
            //判斷是不是Down事件局冰,如果是的話,就要做初始化操作
            if (actionMasked == MotionEvent.ACTION_DOWN) {
                //如果是down事件翁锡,就要清空掉之前的狀態(tài),比如,重置手勢判斷什么的邪财。
                //比如,之前正在判斷是不是一個單點的滑動皂岔,但是第二個down來了速兔,就表示镀迂,不可能是單點的滑動,要重新開始判斷觸摸的手勢
                //清空掉mFirstTouchTarget
                // Throw away all previous state when starting a new touch gesture.
                // The framework may have dropped the up or cancel event for the previous gesture
                // due to an app switch, ANR, or some other state change.
                cancelAndClearTouchTargets(ev);
                resetTouchState();
            }
 
 
            //檢查是否攔截事件
            final boolean intercepted;
            //如果當前是Down事件,或者已經(jīng)有處理Touch事件的目標了
            if (actionMasked == MotionEvent.ACTION_DOWN
                    || mFirstTouchTarget != null) {
                //判斷允不允許這個View攔截
                //使用與運算作為判斷病瞳,可以讓我們在flag中嚎于,存儲好幾個標志
                final boolean disallowIntercept = (mGroupFlags & FLAG_DISALLOW_INTERCEPT) != 0;
                //如果說允許攔截事件
                if (!disallowIntercept) {
                    //確定是不是攔截了
                    intercepted = onInterceptTouchEvent(ev);
                    //重新恢復(fù)Action嫌吠,以免action在上面的步驟被人為地改變了
                    ev.setAction(action); // restore action in case it was changed
                } else {
                    intercepted = false;
                }
            } else {
                // There are no touch targets and this action is not an initial down
                // so this view group continues to intercept touches.
                //如果說档痪,事件已經(jīng)初始化過了锯仪,并且沒有子View被分配處理斥扛,那么就說明,這個ViewGroup已經(jīng)攔截了這個事件
                intercepted = true;
            }
 
            // Check for cancelation.
            //如果viewFlag被設(shè)置了PFLAG_CANCEL_NEXT_UP_EVENT ,那么就表示衬鱼,下一步應(yīng)該是Cancel事件
            //或者如果當前的Action為取消霉颠,那么當前事件應(yīng)該就是取消了。
            final boolean canceled = resetCancelNextUpFlag(this)
                    || actionMasked == MotionEvent.ACTION_CANCEL;
 
            // Update list of touch targets for pointer down, if needed.
            //如果需要(不是取消瞳筏,也沒有被攔截)的話,那么在觸摸down事件的時候更新觸摸目標列表
            //split代表苍柏,當前的ViewGroup是不是支持分割MotionEvent到不同的View當中
            final boolean split = (mGroupFlags & FLAG_SPLIT_MOTION_EVENTS) != 0;
            //新的觸摸對象,
            TouchTarget newTouchTarget = null;
            //是否把事件分配給了新的觸摸
            boolean alreadyDispatchedToNewTouchTarget = false;
            //事件不是取消事件,也沒有攔截那么就要判斷
            if (!canceled && !intercepted) {
                //如果是個全新的Down事件
                //或者是有新的觸摸點
                //或者是光標來回移動事件(不太明白什么時候發(fā)生)
                if (actionMasked == MotionEvent.ACTION_DOWN
                        || (split && actionMasked == MotionEvent.ACTION_POINTER_DOWN)
                        || actionMasked == MotionEvent.ACTION_HOVER_MOVE) {
                    //這個事件的索引,也就是第幾個事件桐罕,如果是down事件就是0
                    final int actionIndex = ev.getActionIndex(); // always 0 for down
                    //獲取分配的ID的bit數(shù)量
                    final int idBitsToAssign = split ? 1 << ev.getPointerId(actionIndex)
                            : TouchTarget.ALL_POINTER_IDS;
 
                    // Clean up earlier touch targets for this pointer id in case they
                    // have become out of sync.
                    //清理之前觸摸這個指針標識,以防他們的目標變得不同步塘淑。
                    removePointersFromTouchTargets(idBitsToAssign);
 
                    final int childrenCount = mChildrenCount;
                    //如果新的觸摸對象為null(這個不是鐵定的嗎)并且當前ViewGroup有子元素
                    if (newTouchTarget == null && childrenCount != 0) {
                        final float x = ev.getX(actionIndex);
                        final float y = ev.getY(actionIndex);
                        // Find a child that can receive the event.
                        // Scan children from front to back.
                        //下面所做的工作,就是找到可以接收這個事件的子元素
                        final View[] children = mChildren;
                        //是否使用自定義的順序來添加控件
                        final boolean customOrder = isChildrenDrawingOrderEnabled();
                        for (int i = childrenCount - 1; i >= 0; i--) {
                            //如果是用了自定義的順序來添加控件,那么繪制的View的順序和mChildren的順序是不一樣的
                            //所以要根據(jù)getChildDrawingOrder取出真正的繪制的View
                            //自定義的繪制,可能第一個會畫到第三個踏施,和第四個定铜,第二個畫到第一個曹动,這樣里面的內(nèi)容和Children是不一樣的
                            final int childIndex = customOrder ?
                                    getChildDrawingOrder(childrenCount, i) : i;
                            final View child = children[childIndex];
                            //如果child不可以接收這個觸摸的事件利花,或者觸摸事件發(fā)生的位置不在這個View的范圍內(nèi)
                            if (!canViewReceivePointerEvents(child)
                                    || !isTransformedTouchPointInView(x, y, child, null)) {
                                continue;
                            }
                            //獲取新的觸摸對象,如果當前的子View在之前的觸摸目標的列表當中就返回touchTarget
                            //子View不在之前的觸摸目標列表那么就返回null
                            newTouchTarget = getTouchTarget(child);
                            if (newTouchTarget != null) {
                                // Child is already receiving touch within its bounds.
                                // Give it the new pointer in addition to the ones it is handling.
                                //如果新的觸摸目標對象不為空橄仍,那么就把這個觸摸的ID賦予它宪哩,這樣子品抽,
                                //這個觸摸的目標對象的id就含有了好幾個pointer的ID了
 
                                newTouchTarget.pointerIdBits |= idBitsToAssign;
                                break;
                            }
                            //如果子View不在之前的觸摸目標列表中,先重置childView的標志,去除掉CACEL的標志
                            resetCancelNextUpFlag(child);
                            //調(diào)用子View的dispatchTouchEvent,并且把pointer的id 賦予進去
                            //如果說嚼鹉,子View接收并且處理了這個事件赐稽,那么就更新上一次觸摸事件的信息,
                            //并且為創(chuàng)建一個新的觸摸目標對象浑侥,并且綁定這個子View和Pointer的ID
                            if (dispatchTransformedTouchEvent(ev, false, child, idBitsToAssign)) {
                                // Child wants to receive touch within its bounds.
                                mLastTouchDownTime = ev.getDownTime();
                                mLastTouchDownIndex = childIndex;
                                mLastTouchDownX = ev.getX();
                                mLastTouchDownY = ev.getY();
                                //這里給mFirstTouchTarget賦值
                                newTouchTarget = addTouchTarget(child, idBitsToAssign);
                                alreadyDispatchedToNewTouchTarget = true;
                                break;
                            }
                        }
                    }
                    //如果newTouchTarget為null姊舵,就代表,這個事件沒有找到子View去處理它寓落,
                    //那么括丁,如果之前已經(jīng)有了觸摸對象(比如,我點了一張圖零如,另一個手指在外面圖的外面點下去)
                    //那么就把這個之前那個觸摸目標定為第一個觸摸對象躏将,并且把這個觸摸(pointer)分配給最近添加的觸摸目標
                    if (newTouchTarget == null && mFirstTouchTarget != null) {
                        // Did not find a child to receive the event.
                        // Assign the pointer to the least recently added target.
                        newTouchTarget = mFirstTouchTarget;
                        while (newTouchTarget.next != null) {
                            newTouchTarget = newTouchTarget.next;
                        }
                        newTouchTarget.pointerIdBits |= idBitsToAssign;
                    }
                }
            }
 
            // Dispatch to touch targets.
            //如果沒有觸摸目標
            if (mFirstTouchTarget == null) {
                // No touch targets so treat this as an ordinary view.
                //那么就表示我們要自己在這個ViewGroup處理這個觸摸事件了
                handled = dispatchTransformedTouchEvent(ev, canceled, null,
                        TouchTarget.ALL_POINTER_IDS);
            } else {
                // Dispatch to touch targets, excluding the new touch target if we already
                // dispatched to it.  Cancel touch targets if necessary.
                TouchTarget predecessor = null;
                TouchTarget target = mFirstTouchTarget;
                //遍歷TouchTargt樹.分發(fā)事件,如果我們已經(jīng)分發(fā)給了新的TouchTarget那么我們就不再分發(fā)給newTouchTarget
                while (target != null) {
                    final TouchTarget next = target.next;
                    if (alreadyDispatchedToNewTouchTarget && target == newTouchTarget) {
                        handled = true;
                    } else {
                        //是否讓child取消處理事件考蕾,如果為true,就會分發(fā)給child一個ACTION_CANCEL事件
                        final boolean cancelChild = resetCancelNextUpFlag(target.child)
                                || intercepted;
                        //派發(fā)事件
                        if (dispatchTransformedTouchEvent(ev, cancelChild,
                                target.child, target.pointerIdBits)) {
                            handled = true;
                        }
                        //cancelChild也就是說会宪,派發(fā)給了當前child一個ACTION_CANCEL事件肖卧,
                        //那么就移除這個child
                        if (cancelChild) {
                            //沒有父節(jié)點,也就是當前是第一個TouchTarget
                            //那么就把頭去掉
                            if (predecessor == null) {
                                mFirstTouchTarget = next;
                            } else {
                                //把下一個賦予父節(jié)點的上一個掸鹅,這樣當前節(jié)點就被丟棄了
                                predecessor.next = next;
                            }
                            //回收內(nèi)存
                            target.recycle();
                            //把下一個賦予現(xiàn)在
                            target = next;
                            //下面的兩行不執(zhí)行了塞帐,因為我們已經(jīng)做了鏈表的操作了。
                            //主要是我們不能執(zhí)行predecessor=target巍沙,因為刪除本節(jié)點的話葵姥,父節(jié)點還是父節(jié)點
                            continue;
                        }
                    }
                    //如果沒有刪除本節(jié)點,那么下一輪父節(jié)點就是當前節(jié)點句携,下一個節(jié)點也是下一輪的當前節(jié)點
                    predecessor = target;
                    target = next;
                }
            }
 
            // Update list of touch targets for pointer up or cancel, if needed.
            //遇到了取消事件榔幸、或者是單點觸摸下情況下手指離開,我們就要更新觸摸的狀態(tài)
            if (canceled
                    || actionMasked == MotionEvent.ACTION_UP
                    || actionMasked == MotionEvent.ACTION_HOVER_MOVE) {
                resetTouchState();
            } else if (split && actionMasked == MotionEvent.ACTION_POINTER_UP) {
                //如果是多點觸摸下的手指抬起事件矮嫉,就要根據(jù)idBit從TouchTarget中移除掉對應(yīng)的Pointer(觸摸點)
                final int actionIndex = ev.getActionIndex();
                final int idBitsToRemove = 1 << ev.getPointerId(actionIndex);
                removePointersFromTouchTargets(idBitsToRemove);
            }
        }
 
        if (!handled && mInputEventConsistencyVerifier != null) {
            mInputEventConsistencyVerifier.onUnhandledEvent(ev, 1);
        }
        return handled;
    }

整體流程:

DOWN事件下削咆,通過輪詢當前ViewGroup的下層View
同步進行判斷,是否在這個View的矩形范圍內(nèi)
如果在的話蠢笋,那么選中這個View進行dispatch轉(zhuǎn)發(fā)拨齐,他是ViewGroup會繼續(xù)轉(zhuǎn)發(fā)下層,如果是View直接消費處理昨寞!
MOVE類型瞻惋,因為其特性的原因厦滤,量大,用輪詢不合適
所以歼狼,在DOWN下來的時候直接記錄當前這個View馁害,然后默認后續(xù)直接轉(zhuǎn)發(fā)這個View
如果手指滑出當前View,那么事件信息傳遞過來的ActionID會改變蹂匹,改變ID后碘菜,進行將target變更!UP\ CANCEL會重置

3限寞、 View
源碼分析

/**
     * Pass the touch screen motion event down to the target view, or this
     * view if it is the target.
     * 將屏幕的按壓事件傳遞給目標view忍啸,或者當前view即目標view
     * @param event The motion event to be dispatched.
     * @return True if the event was handled by the view, false otherwise.
     */
    public boolean dispatchTouchEvent(MotionEvent event) {
        //最前面這一段就是判斷當前事件是否能獲得焦點,
        // 如果不能獲得焦點或者不存在一個View履植,那我們就直接返回False跳出循環(huán)
        // If the event should be handled by accessibility focus first.
        if (event.isTargetAccessibilityFocus()) {
            // We don't have focus or no virtual descendant has it, do not handle the event.
            if (!isAccessibilityFocusedViewOrHost()) {
                return false;
            }
            // We have focus and got the event, then use normal event dispatch.
            event.setTargetAccessibilityFocus(false);
        }
        //設(shè)置返回的默認值
        boolean result = false;
        //這段是系統(tǒng)調(diào)試方面计雌,可以直接忽略
        if (mInputEventConsistencyVerifier != null) {
            mInputEventConsistencyVerifier.onTouchEvent(event, 0);
        }

        //Android用一個32位的整型值表示一次TouchEvent事件,低8位表示touch事件的具體動作,比如按下玫霎,抬起凿滤,滑動,還有多點觸控時的按下庶近,抬起翁脆,這個和單點是區(qū)分開的,下面看具體的方法:
        //1 getAction:觸摸動作的原始32位信息鼻种,包括事件的動作反番,觸控點信息
        //2 getActionMasked:觸摸的動作,按下,抬起叉钥,滑動罢缸,多點按下,多點抬起
        //3 getActionIndex:觸控點信息
        final int actionMasked = event.getActionMasked();
        if (actionMasked == MotionEvent.ACTION_DOWN) {
            //當我們手指按到View上時,其他的依賴滑動都要先停下
            // Defensive cleanup for new gesture
            stopNestedScroll();
        }
        //過濾掉一些不合法的事件,比如當前的View的窗口被遮擋了投队。
        if (onFilterTouchEventForSecurity(event)) {
            //ListenerInfo 是view的一個內(nèi)部類 里面有各種各樣的listener,
            // 例如OnClickListener枫疆,OnLongClickListener,OnTouchListener等等
            ListenerInfo li = mListenerInfo;

            //首先判斷如果監(jiān)聽li對象!=null 且我們通過setOnTouchListener設(shè)置了監(jiān)聽敷鸦,
            // 即是否有實現(xiàn)OnTouchListener息楔,
            // 如果有實現(xiàn)就判斷當前的view狀態(tài)是不是ENABLED,
            // 如果實現(xiàn)的OnTouchListener的onTouch中返回true,并處理事件轧膘,則
            if (li != null && li.mOnTouchListener != null
                    && (mViewFlags & ENABLED_MASK) == ENABLED
                    && li.mOnTouchListener.onTouch(this, event)) {
                //如果滿足這些條件那么返回true钞螟,這個事件就在此處理意味著這個View需要事件分發(fā)
                result = true;
            }
            //如果上一段判斷的條件沒有滿足(沒有在代碼里面setOnTouchListener的話),
            // 就判斷View自身的onTouchEvent方法有沒有處理谎碍,沒有處理最后返回false鳞滨,處理了返回true;
            //也就是前面的判斷優(yōu)先級更高

            if (!result && onTouchEvent(event)) {
                result = true;
            }
        }
        //系統(tǒng)調(diào)試分析相關(guān)蟆淀,沒有影響
        if (!result && mInputEventConsistencyVerifier != null) {
            mInputEventConsistencyVerifier.onUnhandledEvent(event, 0);
        }

        // Clean up after nested scrolls if this is the end of a gesture;
        // also cancel it if we tried an ACTION_DOWN but we didn't want the rest
        // of the gesture.
        //如果這是手勢的結(jié)尾拯啦,則在嵌套滾動后清理
        if (actionMasked == MotionEvent.ACTION_UP ||
                actionMasked == MotionEvent.ACTION_CANCEL ||
                (actionMasked == MotionEvent.ACTION_DOWN && !result)) {
            stopNestedScroll();
        }

        return result;
    }

先調(diào)用listener的onTouch澡匪,而onTouchEvent的調(diào)用是根據(jù)onTouch結(jié)果走,
onTouch的使用優(yōu)先級高于onTouchEvent褒链,onTouch通過返回值可以控制onTouchEvent的調(diào)用唁情。

事件消費過程

U型模型,事件從上到下傳遞甫匹,從下到上消費(無消費)
L型模型甸鸟,事件從上到下傳遞,后被攔截(有消費)

總結(jié)

事件分發(fā)就是事件從linux層通過驅(qū)動采集數(shù)據(jù)兵迅,底層使用epoll和inotify傳遞出來抢韭,F(xiàn)rameWork層通過InputReaderThread讀取,通過InputDispatcherThread分發(fā)到WMS恍箭,WMS將事件傳遞到Activity刻恭,上層的Activity、ViewGroup扯夭、View之間事件的分發(fā)和消費鳍贾。

具體流程:

1、事件信號是物理文件存儲數(shù)據(jù)交洗,位置:dev/input
2骑科、linux有提供相關(guān)的文件監(jiān)控api,其中使用了inotify(能監(jiān)控文件變化產(chǎn)生FD) 和epoll(可以監(jiān)控FD藕筋,以此配合完成文件的監(jiān)控與監(jiān)聽)
3纵散、Android寫了兩個線程來處理dev/input下面的信號(InputReaderThread和InputDispathcerThread)
4、專門寫了一個EventHub對象隐圾,里面用inotify+epoll對dev/input下進行監(jiān)控!
5掰茶、將該對象放到InputReaderThread當中去執(zhí)行暇藏,輪訓(xùn)getEvent(),這個里面有epoll_wait,相當于wait-notify機制濒蒋,喚醒的觸發(fā)點是/dev/input下的文件被改變盐碱,這個文件由驅(qū)動去推送數(shù)據(jù)
6、InputReaderThread當中將/dev/input下的數(shù)據(jù)提取沪伙,封裝瓮顽,然后交給InputDispathcerThread
7、InputDispathcerThread給最終選擇到對應(yīng)的ViewRootImpl(Window)
8围橡、中間的通信機制通過socketpair進行暖混,兩邊一人一組socketpair
9、然后在ViewRootImpl中對于Channel的連接的文件進行監(jiān)控翁授,一次導(dǎo)致能夠最終上層接受到touch信號拣播!

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末晾咪,一起剝皮案震驚了整個濱河市,隨后出現(xiàn)的幾起案子贮配,更是在濱河造成了極大的恐慌谍倦,老刑警劉巖,帶你破解...
    沈念sama閱讀 222,627評論 6 517
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件泪勒,死亡現(xiàn)場離奇詭異昼蛀,居然都是意外死亡,警方通過查閱死者的電腦和手機圆存,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 95,180評論 3 399
  • 文/潘曉璐 我一進店門叼旋,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人辽剧,你說我怎么就攤上這事送淆。” “怎么了怕轿?”我有些...
    開封第一講書人閱讀 169,346評論 0 362
  • 文/不壞的土叔 我叫張陵偷崩,是天一觀的道長。 經(jīng)常有香客問我撞羽,道長阐斜,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 60,097評論 1 300
  • 正文 為了忘掉前任诀紊,我火速辦了婚禮谒出,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘邻奠。我一直安慰自己笤喳,他們只是感情好,可當我...
    茶點故事閱讀 69,100評論 6 398
  • 文/花漫 我一把揭開白布碌宴。 她就那樣靜靜地躺著杀狡,像睡著了一般。 火紅的嫁衣襯著肌膚如雪贰镣。 梳的紋絲不亂的頭發(fā)上呜象,一...
    開封第一講書人閱讀 52,696評論 1 312
  • 那天,我揣著相機與錄音碑隆,去河邊找鬼恭陡。 笑死,一個胖子當著我的面吹牛上煤,可吹牛的內(nèi)容都是我干的休玩。 我是一名探鬼主播,決...
    沈念sama閱讀 41,165評論 3 422
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼哥捕!你這毒婦竟也來了牧抽?” 一聲冷哼從身側(cè)響起,我...
    開封第一講書人閱讀 40,108評論 0 277
  • 序言:老撾萬榮一對情侶失蹤遥赚,失蹤者是張志新(化名)和其女友劉穎扬舒,沒想到半個月后,有當?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體凫佛,經(jīng)...
    沈念sama閱讀 46,646評論 1 319
  • 正文 獨居荒郊野嶺守林人離奇死亡讲坎,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 38,709評論 3 342
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發(fā)現(xiàn)自己被綠了愧薛。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片晨炕。...
    茶點故事閱讀 40,861評論 1 353
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖毫炉,靈堂內(nèi)的尸體忽然破棺而出瓮栗,到底是詐尸還是另有隱情,我是刑警寧澤瞄勾,帶...
    沈念sama閱讀 36,527評論 5 351
  • 正文 年R本政府宣布费奸,位于F島的核電站,受9級特大地震影響进陡,放射性物質(zhì)發(fā)生泄漏愿阐。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點故事閱讀 42,196評論 3 336
  • 文/蒙蒙 一趾疚、第九天 我趴在偏房一處隱蔽的房頂上張望缨历。 院中可真熱鬧,春花似錦糙麦、人聲如沸辛孵。這莊子的主人今日做“春日...
    開封第一講書人閱讀 32,698評論 0 25
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽觉吭。三九已至,卻和暖如春仆邓,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背伴鳖。 一陣腳步聲響...
    開封第一講書人閱讀 33,804評論 1 274
  • 我被黑心中介騙來泰國打工节值, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留,地道東北人榜聂。 一個月前我還...
    沈念sama閱讀 49,287評論 3 379
  • 正文 我出身青樓搞疗,卻偏偏與公主長得像,于是被迫代替她去往敵國和親须肆。 傳聞我的和親對象是個殘疾皇子匿乃,可洞房花燭夜當晚...
    茶點故事閱讀 45,860評論 2 361

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