Android中UI的繪制流程

前言

我們都知道Android的View的工作流程主要是指measure、layout、draw這三大流程数初,即測(cè)量、布局和繪制梗顺,其中measure確定View的測(cè)量寬高妙真,layout根據(jù)測(cè)量的寬高確定View在其父View中的四個(gè)頂點(diǎn)的位置,而draw則將View繪制到屏幕上荚守,這樣通過ViewGroup的遞歸遍歷珍德,一個(gè)View樹就展現(xiàn)在屏幕上了。

Android的視圖結(jié)構(gòu)

Window的基本概念

Window表示的是一個(gè)窗口的概念矗漾,它是站在WindowManagerService角度上的一個(gè)抽象的概念锈候,Android中所有的視圖都是通過Window來呈現(xiàn)的,不管是Activity敞贡、Dialog還是Toast泵琳,只要有View的地方就一定有Window。

這里需要注意的是誊役,這個(gè)抽象的Window概念和PhoneWindow這個(gè)類并不是同一個(gè)東西获列,PhoneWindow表示的是手機(jī)屏幕的抽象,它充當(dāng)Activity和DecorView之間的媒介蛔垢,就算沒有PhoneWindow也是可以展示View的击孩。

拋開一切,僅站在WindowManagerService的角度上鹏漆,Android的界面就是由一個(gè)個(gè)Window層疊展現(xiàn)的巩梢,而Window又是一個(gè)抽象的概念,它并不是實(shí)際存在的艺玲,它是以View的形式存在括蝠,這個(gè)View就是DecorView。

DecorView的概念

DecorView是整個(gè)Window界面的最頂層View饭聚,View的測(cè)量忌警、布局、繪制秒梳、事件分發(fā)都是由DecorView往下遍歷這個(gè)View樹法绵。DecorView作為頂級(jí)View,一般情況下它內(nèi)部會(huì)包含一個(gè)豎直方向的LinearLayout端幼,在這個(gè)LinearLayout里面有上下兩個(gè)部分(具體情況和Android的版本及主題有關(guān))礼烈,上面是【標(biāo)題欄】,下面是【內(nèi)容欄】婆跑。在Activity中我們通過setContentView所設(shè)置的布局文件其實(shí)就是被加載到【內(nèi)容欄】中的此熬,而內(nèi)容欄的id是content,因此指定布局的方法叫setContent().

ViewRoot的概念
ViewRoot對(duì)應(yīng)于ViewRootImpl類,它是連接WindowManager和DecorView的紐帶犀忱,View的三大流程均是通過ViewRoot來完成的募谎。在ActivityThread中,當(dāng)Activity對(duì)象被創(chuàng)建完之后阴汇,會(huì)將DecorView添加到Window中数冬,同時(shí)會(huì)創(chuàng)建對(duì)應(yīng)的ViewRootImpl,并將ViewRootImpl和DecorView建立關(guān)聯(lián)搀庶,并保存到WindowManagerGlobal對(duì)象中拐纱。

View的繪制流程是從ViewRoot的performTraversals方法開始的,它經(jīng)過measure哥倔、layout和draw三個(gè)過程才能最終將一個(gè)View繪制出來秸架,大致流程如下圖:

一、Activity的啟動(dòng)流程

所有Android應(yīng)用都是由多個(gè)Activity和Fragment組成咆蒿,而一個(gè)頁面的承載都是由Activity去承載东抹。我們知道了上訴幾個(gè)概念之后,就可以來分析一下Activity是怎么由創(chuàng)建到UI全部渲染出來的過程沃测。下午是我閱讀源碼分析得出的Activity時(shí)序圖缭黔,有興趣的可以自己去看,文章里不再貼代碼蒂破。

從上面的時(shí)序圖馏谨,可以得知,渲染的最關(guān)鍵一步是通過WindowManager實(shí)例把之前創(chuàng)建的DecorView實(shí)例添加到根視圖中寞蚌,下面我們?cè)敿?xì)來看這塊的代碼的執(zhí)行過程:

可以得知Activity最后的渲染是通過ViewRootImpl來實(shí)現(xiàn)計(jì)算田巴、布局、繪制到屏幕挟秤。

二、UI的刷新

上一章節(jié)抄伍,我們知道了Activity的啟動(dòng)到頁面的整體UI展現(xiàn)到屏幕上的流程艘刚。那么Activity是怎么實(shí)現(xiàn)UI頁面的刷新的呢?

我們知道Android上的刷新無非3種方法invalidate()截珍、postInvalidate()攀甚、requestLayout(),而各種控件最后調(diào)用的刷新方式岗喉,也是通過這3種方法來實(shí)現(xiàn)秋度。

  • invalidate()

postInvalidate()和invalidate()區(qū)別在于,invalidate()只能UI線程中調(diào)用钱床,而postInvalidate()可以在子線程中調(diào)用荚斯。postInvalidate()最后其實(shí)通過ViewRootImpl里的handler切換到UI線程,最終執(zhí)行invalidate()。invalidate()只會(huì)觸發(fā)視圖的onDraw()方法事期,而不會(huì)觸發(fā)onMeasure()滥壕、onLayout()。

所以我們只需要分析invalidate()即可兽泣,我們首先來看View中invalidate()的部分源碼:

    public void invalidate() {
        invalidate(true);
    }

    public void invalidate(boolean invalidateCache) {
        //invalidateCache 使繪制緩存失效
        invalidateInternal(0, 0, mRight - mLeft, mBottom - mTop, invalidateCache, true);
    }


    void invalidateInternal(int l, int t, int r, int b, boolean invalidateCache,
                            boolean fullInvalidate) {
        ...
        //設(shè)置了跳過繪制標(biāo)記
        if (skipInvalidate()) {
            return;
        }

        //PFLAG_DRAWN 表示此前該View已經(jīng)繪制過 PFLAG_HAS_BOUNDS表示該View已經(jīng)layout過绎橘,確定過坐標(biāo)了
        if ((mPrivateFlags & (PFLAG_DRAWN | PFLAG_HAS_BOUNDS)) == (PFLAG_DRAWN | PFLAG_HAS_BOUNDS)
                || (invalidateCache && (mPrivateFlags & PFLAG_DRAWING_CACHE_VALID) == PFLAG_DRAWING_CACHE_VALID)
                || (mPrivateFlags & PFLAG_INVALIDATED) != PFLAG_INVALIDATED
                || (fullInvalidate && isOpaque() != mLastIsOpaque)) {
            if (fullInvalidate) {
                //默認(rèn)true
                mLastIsOpaque = isOpaque();
                //清除繪制標(biāo)記
                mPrivateFlags &= ~PFLAG_DRAWN;
            }

            //需要繪制
            mPrivateFlags |= PFLAG_DIRTY;

            if (invalidateCache) {
                //1、加上繪制失效標(biāo)記
                //2唠倦、清除繪制緩存有效標(biāo)記
                //這兩標(biāo)記在硬件加速繪制分支用到
                mPrivateFlags |= PFLAG_INVALIDATED;
                mPrivateFlags &= ~PFLAG_DRAWING_CACHE_VALID;
            }
            
            final AttachInfo ai = mAttachInfo;
            final ViewParent p = mParent;
            if (p != null && ai != null && l < r && t < b) {
                final Rect damage = ai.mTmpInvalRect;
                //記錄需要重新繪制的區(qū)域 damge称鳞,該區(qū)域?yàn)樵揤iew尺寸
                damage.set(l, t, r, b);
                //p 為該View的父布局
                //調(diào)用父布局的invalidateChild
                p.invalidateChild(this, damage);
            }
            ...
        }
    }

從上可知,當(dāng)前要刷新的View確定了刷新區(qū)域后即調(diào)用了父布局的invalidateChild(xx)方法稠鼻。該方法為ViewGroup里的final方法冈止。

    public final void invalidateChild(View child, final Rect dirty) {
        final AttachInfo attachInfo = mAttachInfo;
        if (attachInfo != null && attachInfo.mHardwareAccelerated) {
            //1、如果是支持硬件加速枷餐,則走該分支
            onDescendantInvalidated(child, child);
            return;
        }
        //2靶瘸、軟件繪制
        ViewParent parent = this;
        if (attachInfo != null) {
            //動(dòng)畫相關(guān),忽略
            ...
            do {
                View view = null;
                if (parent instanceof View) {
                    view = (View) parent;
                }
                ...
                parent = parent.invalidateChildInParent(location, dirty);
                //動(dòng)畫相關(guān)
            } while (parent != null);
        }
    }

由上可知毛肋,在該方法里區(qū)分了硬件加速繪制與軟件繪制怨咪,分別來看看兩者區(qū)別。

硬件加速繪制分支
如果該Window支持硬件加速润匙,則走下邊流程:

    public void onDescendantInvalidated(@NonNull View child, @NonNull View target) {
        mPrivateFlags |= (target.mPrivateFlags & PFLAG_DRAW_ANIMATION);
        
        if ((target.mPrivateFlags & ~PFLAG_DIRTY_MASK) != 0) {
           //此處都會(huì)走
            mPrivateFlags = (mPrivateFlags & ~PFLAG_DIRTY_MASK) | PFLAG_DIRTY;
            //清除繪制緩存有效標(biāo)記
            mPrivateFlags &= ~PFLAG_DRAWING_CACHE_VALID;
        }
        
        if (mLayerType == LAYER_TYPE_SOFTWARE) {
            //如果是開啟了軟件繪制诗眨,則加上繪制失效標(biāo)記
            mPrivateFlags |= PFLAG_INVALIDATED | PFLAG_DIRTY;
            //更改target指向
            target = this;
        }

        if (mParent != null) {
            //調(diào)用父布局的onDescendantInvalidated
            mParent.onDescendantInvalidated(this, target);
        }
    }

onDescendantInvalidated 方法的目的是不斷向上尋找其父布局,并將父布局PFLAG_DRAWING_CACHE_VALID 標(biāo)記清空孕讳,也就是繪制緩存清空匠楚。
而我們知道,根View的mParent指向ViewRootImpl對(duì)象厂财,因此來看看它里面的onDescendantInvalidated()方法芋簿。

    @Override
    public void onDescendantInvalidated(@NonNull View child, @NonNull View descendant) {
        // TODO: Re-enable after camera is fixed or consider targetSdk checking this
        // checkThread();
        if ((descendant.mPrivateFlags & PFLAG_DRAW_ANIMATION) != 0) {
            mIsAnimating = true;
        }
        invalidate();
    }

    @UnsupportedAppUsage
    void invalidate() {
        //mDirty 為臟區(qū)域,也就是需要重繪的區(qū)域
        //mWidth璃饱,mHeight 為Window尺寸
        mDirty.set(0, 0, mWidth, mHeight);
        if (!mWillDrawSoon) {
            //開啟View 三大流程
            scheduleTraversals();
        }
    }

軟件繪制分支
如果該Window不支持硬件加速与斤,那么走軟件繪制分支:
parent.invalidateChildInParent(location, dirty) 返回mParent,只要mParent不為空那么一直調(diào)用invalidateChildInParent(xx)荚恶,實(shí)際上這也是遍歷ViewTree過程撩穿,來看看關(guān)鍵invalidateChildInParent(xx)

 public ViewParent invalidateChildInParent(final int[] location, final Rect dirty) {
        //dirty 為失效的區(qū)域,也就是需要重繪的區(qū)域
        if ((mPrivateFlags & (PFLAG_DRAWN | PFLAG_DRAWING_CACHE_VALID)) != 0) {
            //該View繪制過或者繪制緩存有效
            if ((mGroupFlags & (FLAG_OPTIMIZE_INVALIDATE | FLAG_ANIMATION_DONE))
                    != FLAG_OPTIMIZE_INVALIDATE) {
                //修正重繪的區(qū)域
                dirty.offset(location[CHILD_LEFT_INDEX] - mScrollX,
                        location[CHILD_TOP_INDEX] - mScrollY);
                if ((mGroupFlags & FLAG_CLIP_CHILDREN) == 0) {
                    //如果允許子布局超過父布局區(qū)域展示
                    //則該dirty 區(qū)域需要擴(kuò)大
                    dirty.union(0, 0, mRight - mLeft, mBottom - mTop);
                }
                final int left = mLeft;
                final int top = mTop;
                if ((mGroupFlags & FLAG_CLIP_CHILDREN) == FLAG_CLIP_CHILDREN) {
                    //默認(rèn)會(huì)走這
                    //如果不允許子布局超過父布局區(qū)域展示谒撼,則取相交區(qū)域
                    if (!dirty.intersect(0, 0, mRight - left, mBottom - top)) {
                        dirty.setEmpty();
                    }
                }
                //記錄偏移食寡,用以不斷修正重繪區(qū)域,使之相對(duì)計(jì)算出相對(duì)屏幕的坐標(biāo)
                location[CHILD_LEFT_INDEX] = left;
                location[CHILD_TOP_INDEX] = top;
            } else {
                ...
            }
            //標(biāo)記緩存失效
            mPrivateFlags &= ~PFLAG_DRAWING_CACHE_VALID;
            if (mLayerType != LAYER_TYPE_NONE) {
                //如果設(shè)置了緩存類型廓潜,則標(biāo)記該View需要重繪
                mPrivateFlags |= PFLAG_INVALIDATED;
            }
            //返回父布局
            return mParent;
        }
        return null;
    }

與硬件加速繪制一致抵皱,最終調(diào)用ViewRootImpl invalidateChildInParent(xx)善榛,來看看實(shí)現(xiàn)

    public ViewParent invalidateChildInParent(int[] location, Rect dirty) {
        checkThread();
        if (DEBUG_DRAW) Log.v(mTag, "Invalidate child: " + dirty);
        if (dirty == null) {
            //臟區(qū)域?yàn)榭眨瑒t默認(rèn)刷新整個(gè)窗口
            invalidate();
            return null;
        } else if (dirty.isEmpty() && !mIsAnimating) {
            return null;
        }
        ...
        invalidateRectOnScreen(dirty);
        return null;
    }

    private void invalidateRectOnScreen(Rect dirty) {
        final Rect localDirty = mDirty;
        //合并臟區(qū)域叨叙,取并集
        localDirty.union(dirty.left, dirty.top, dirty.right, dirty.bottom);
        ...
        if (!mWillDrawSoon && (intersected || mIsAnimating)) {
            //開啟View的三大繪制流程
            scheduleTraversals();
        }
    }
  • requestLayout()
    顧名思義锭弊,重新請(qǐng)求布局。來看看View.requestLayout()方法
    public void requestLayout() {
        //清空測(cè)量緩存
        if (mMeasureCache != null) mMeasureCache.clear();
        ...
        //添加強(qiáng)制layout 標(biāo)記擂错,該標(biāo)記觸發(fā)layout
        mPrivateFlags |= PFLAG_FORCE_LAYOUT;
        //添加重繪標(biāo)記
        mPrivateFlags |= PFLAG_INVALIDATED;

        if (mParent != null && !mParent.isLayoutRequested()) {
            //如果上次的layout 請(qǐng)求已經(jīng)完成
            //父布局繼續(xù)調(diào)用requestLayout
            mParent.requestLayout();
        }
        ...
    }

可以看出味滞,這個(gè)遞歸調(diào)用和invalidate一樣的套路,向上尋找其父布局钮呀,一直到ViewRootImpl為止剑鞍,給每個(gè)布局設(shè)置PFLAG_FORCE_LAYOUT和PFLAG_INVALIDATED標(biāo)記。查看ViewRootImpl requestLayout()

    public void requestLayout() {
        //是否正在進(jìn)行l(wèi)ayout過程
        if (!mHandlingLayoutInLayoutRequest) {
            //檢查線程是否一致
            checkThread();
            //標(biāo)記有一次layout的請(qǐng)求
            mLayoutRequested = true;
            //開啟View 三大流程
            scheduleTraversals();
        }
    }

很明顯爽醋,requestLayout目的很單純:

  • 1蚁署、向上尋找父布局、并設(shè)置強(qiáng)制layout標(biāo)記
  • 2蚂四、最終開啟三大繪制流程

和invalidate()一樣的配方光戈,當(dāng)刷新信號(hào)來到之時(shí),調(diào)用doTraversal()->performTraversals()遂赠,而在performTraversals()里真正執(zhí)行三大流程久妆。

    private void performTraversals() {
        //mLayoutRequested 在requestLayout時(shí)賦值為true
        boolean layoutRequested = mLayoutRequested && (!mStopped || mReportNextDraw);
        if (layoutRequested) {
            //measure 過程
            windowSizeMayChange |= measureHierarchy(host, lp, res,
                    desiredWindowWidth, desiredWindowHeight);
        }
        ...

        final boolean didLayout = layoutRequested && (!mStopped || mReportNextDraw);
        if (didLayout) {
            //layout 過程
            performLayout(lp, mWidth, mHeight);
        }
        ...
    }

由此可見:

  • 1、requestLayout 最終將會(huì)觸發(fā)Measure跷睦、Layout 過程筷弦。
  • 2、由于沒有設(shè)置重繪區(qū)域抑诸,因此Draw 過程將不會(huì)觸發(fā)烂琴。

之前設(shè)置的PFLAG_FORCE_LAYOUT標(biāo)記有啥用呢?回憶一下measure 過程:

    public final void measure(int widthMeasureSpec, int heightMeasureSpec) {
        ...
        //requestLayout時(shí)蜕乡,PFLAG_FORCE_LAYOUT 標(biāo)記被設(shè)置
        final boolean forceLayout = (mPrivateFlags & PFLAG_FORCE_LAYOUT) == PFLAG_FORCE_LAYOUT;
        ...
        if (forceLayout || needsLayout) {
            ...
            int cacheIndex = forceLayout ? -1 : mMeasureCache.indexOfKey(key);
            if (cacheIndex < 0 || sIgnoreMeasureCache) {
                //測(cè)量
                onMeasure(widthMeasureSpec, heightMeasureSpec);
            } else {
                ...
            }
            ...
            }
        }
    }

PFLAG_FORCE_LAYOUT 標(biāo)記打上之后奸绷,會(huì)觸發(fā)onMeasure()測(cè)量自身及其子布局。

試想一下层玲,假設(shè)View的尺寸改變了健盒,變大了,那么調(diào)用了requestLayout后因?yàn)樽吡薓easure称簿、Layout 過程,測(cè)量惰帽、擺放倒是重新設(shè)置了憨降,但是不調(diào)用Draw出不來效果啊。實(shí)際上该酗,View layout時(shí)候已經(jīng)考慮到了授药。在View.layout(xx)->setFrame(xx)里

    protected boolean setFrame(int left, int top, int right, int bottom) {
        boolean changed = false;

        if (mLeft != left || mRight != right || mTop != top || mBottom != bottom) {
            changed = true;
            ...
            int oldWidth = mRight - mLeft;
            int oldHeight = mBottom - mTop;
            int newWidth = right - left;
            int newHeight = bottom - top;
            boolean sizeChanged = (newWidth != oldWidth) || (newHeight != oldHeight);
            //尺寸發(fā)生改變 調(diào)用invalidate 傳入true士嚎,否則傳入false
            invalidate(sizeChanged);
            ...
        }
        ...
        return changed;
    }

也就是說:

  • 1、requestLayout調(diào)用后悔叽,可能會(huì)觸發(fā)invalidate莱衩。
  • 2、若是觸發(fā)了invalidate()娇澎,不管傳入true還是false笨蚁,都會(huì)走重繪流程。

總結(jié)

  • invalidate調(diào)用后只會(huì)觸發(fā)Draw 過程趟庄。
  • requestLayout 會(huì)觸發(fā)Measure括细、Layout過程,如果尺寸發(fā)生改變戚啥,則會(huì)調(diào)用invalidate奋单。
  • 當(dāng)涉及View的尺寸、位置變化時(shí)使用requestLayout猫十。
  • 當(dāng)僅僅需要重繪時(shí)調(diào)用invalidate览濒。
  • 如果不確定requestLayout 是否觸發(fā)invalidate,可在requestLayout后繼續(xù)調(diào)用invalidate拖云。

三贷笛、自定義ViewGroup的onDraw為什么不走?

由前面的幾節(jié)內(nèi)容可以知道江兢,所有的視圖的繪制最終都會(huì)經(jīng)過ViewRootImpl來實(shí)現(xiàn)昨忆,最后都會(huì)走到View的onDraw方法里,下面我們?cè)敿?xì)來看源碼:

 public void draw(Canvas canvas) {
        final int privateFlags = mPrivateFlags;
        final boolean dirtyOpaque = (privateFlags & PFLAG_DIRTY_MASK) == PFLAG_DIRTY_OPAQUE &&
                (mAttachInfo == null || !mAttachInfo.mIgnoreDirtyState);
        mPrivateFlags = (privateFlags & ~PFLAG_DIRTY_MASK) | PFLAG_DRAWN;

        /*
         * Draw traversal performs several drawing steps which must be executed
         * in the appropriate order:
         *
         *      1. Draw the background
         *      2. If necessary, save the canvas' layers to prepare for fading
         *      3. Draw view's content
         *      4. Draw children
         *      5. If necessary, draw the fading edges and restore layers
         *      6. Draw decorations (scrollbars for instance)
         */

        // Step 1, draw the background, if needed
        int saveCount;

        if (!dirtyOpaque) {
            drawBackground(canvas);
        }

        // skip step 2 & 5 if possible (common case)
        final int viewFlags = mViewFlags;
        boolean horizontalEdges = (viewFlags & FADING_EDGE_HORIZONTAL) != 0;
        boolean verticalEdges = (viewFlags & FADING_EDGE_VERTICAL) != 0;
        if (!verticalEdges && !horizontalEdges) {
            // Step 3, draw the content
            if (!dirtyOpaque) onDraw(canvas);//如果有背景色杉允,走onDraw()方法邑贴,如果沒有背景色,不走onDraw()方法

            // Step 4, draw the children
            dispatchDraw(canvas);

            // Overlay is part of the content and draws beneath Foreground
            if (mOverlay != null && !mOverlay.isEmpty()) {
                mOverlay.getOverlayView().dispatchDraw(canvas);
            }

            // Step 6, draw decorations (foreground, scrollbars)
            onDrawForeground(canvas);

            // we're done...
            return;
        }
        ......
    }

從代碼的注釋中能清楚的看到繪制的順序:

1叔磷、畫背景
2拢驾、畫canvas的圖層(非必須)
3、畫View自己
4改基、畫子View
5繁疤、如果2執(zhí)行了,這步要回復(fù)圖層(非必須)

這個(gè)并不是重點(diǎn)秕狰,下面的這塊代碼才是重點(diǎn):

  //如果有背景色稠腊,走onDraw()方法,如果沒有背景色鸣哀,不走onDraw()方法
  if (!dirtyOpaque) onDraw(canvas);

  // Step 4, draw the children
  dispatchDraw(canvas);

總結(jié)

1架忌、自定義ViewGroup的onDraw()方法需要設(shè)置背景才會(huì)調(diào)用
2、自定義ViewGroup正常情況下應(yīng)該實(shí)現(xiàn)dispatchDraw()

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末我衬,一起剝皮案震驚了整個(gè)濱河市叹放,隨后出現(xiàn)的幾起案子饰恕,更是在濱河造成了極大的恐慌,老刑警劉巖井仰,帶你破解...
    沈念sama閱讀 219,110評(píng)論 6 508
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件埋嵌,死亡現(xiàn)場(chǎng)離奇詭異,居然都是意外死亡俱恶,警方通過查閱死者的電腦和手機(jī)雹嗦,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 93,443評(píng)論 3 395
  • 文/潘曉璐 我一進(jìn)店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來速那,“玉大人俐银,你說我怎么就攤上這事《搜觯” “怎么了捶惜?”我有些...
    開封第一講書人閱讀 165,474評(píng)論 0 356
  • 文/不壞的土叔 我叫張陵,是天一觀的道長(zhǎng)荔烧。 經(jīng)常有香客問我吱七,道長(zhǎng),這世上最難降的妖魔是什么鹤竭? 我笑而不...
    開封第一講書人閱讀 58,881評(píng)論 1 295
  • 正文 為了忘掉前任踊餐,我火速辦了婚禮,結(jié)果婚禮上臀稚,老公的妹妹穿的比我還像新娘吝岭。我一直安慰自己,他們只是感情好吧寺,可當(dāng)我...
    茶點(diǎn)故事閱讀 67,902評(píng)論 6 392
  • 文/花漫 我一把揭開白布窜管。 她就那樣靜靜地躺著,像睡著了一般稚机。 火紅的嫁衣襯著肌膚如雪幕帆。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 51,698評(píng)論 1 305
  • 那天赖条,我揣著相機(jī)與錄音失乾,去河邊找鬼。 笑死纬乍,一個(gè)胖子當(dāng)著我的面吹牛碱茁,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播仿贬,決...
    沈念sama閱讀 40,418評(píng)論 3 419
  • 文/蒼蘭香墨 我猛地睜開眼早芭,長(zhǎng)吁一口氣:“原來是場(chǎng)噩夢(mèng)啊……” “哼!你這毒婦竟也來了诅蝶?” 一聲冷哼從身側(cè)響起退个,我...
    開封第一講書人閱讀 39,332評(píng)論 0 276
  • 序言:老撾萬榮一對(duì)情侶失蹤,失蹤者是張志新(化名)和其女友劉穎调炬,沒想到半個(gè)月后语盈,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 45,796評(píng)論 1 316
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡缰泡,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 37,968評(píng)論 3 337
  • 正文 我和宋清朗相戀三年刀荒,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片棘钞。...
    茶點(diǎn)故事閱讀 40,110評(píng)論 1 351
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡缠借,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出宜猜,到底是詐尸還是另有隱情泼返,我是刑警寧澤,帶...
    沈念sama閱讀 35,792評(píng)論 5 346
  • 正文 年R本政府宣布姨拥,位于F島的核電站绅喉,受9級(jí)特大地震影響,放射性物質(zhì)發(fā)生泄漏叫乌。R本人自食惡果不足惜柴罐,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,455評(píng)論 3 331
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望憨奸。 院中可真熱鬧革屠,春花似錦、人聲如沸排宰。這莊子的主人今日做“春日...
    開封第一講書人閱讀 32,003評(píng)論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽额各。三九已至国觉,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間虾啦,已是汗流浹背麻诀。 一陣腳步聲響...
    開封第一講書人閱讀 33,130評(píng)論 1 272
  • 我被黑心中介騙來泰國(guó)打工, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留傲醉,地道東北人蝇闭。 一個(gè)月前我還...
    沈念sama閱讀 48,348評(píng)論 3 373
  • 正文 我出身青樓,卻偏偏與公主長(zhǎng)得像硬毕,于是被迫代替她去往敵國(guó)和親呻引。 傳聞我的和親對(duì)象是個(gè)殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 45,047評(píng)論 2 355

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