《深入理解Android卷 I》- 第八章 - Surface- 读书笔记-part1

概述

分析surface系统主要从两条线分析

​ 应用程序和surface的关系

SurfaceSurfaceFlinger之间的关系

在应用中,不管是二维图像还是三维图像,应用最终都要和Surface交互,Surface就像UI画布,应用则像是在Surface上作画

SurfaceSurfaceFlingerSurfaceSurfaceFlinger提供数据,SurfaceFlinger则混合数据。

1 一个Activity的显示

1.1 Activity的创建

在之前的Zygote分析中我们知道,当Zygote接受到请求后会fork出一个子进程,这个子进程的入口函数就是ActivityThreadmain函数,这就是一个应用开启的地方。ActivityThread类中有一个handleLaunchActivity()函数,他就是创建Activity的地方

frameworks/base/core/java/android/app/ActivityThread.java

private void handleLaunchActivity(ActivityClientRecord r, Intent customIntent, String reason) {
        // If we are getting ready to gc after going to the background, well
        // we are back active so skip it.
        unscheduleGcIdler();
        mSomeActivitiesChanged = true;

        if (r.profilerInfo != null) {
            mProfiler.setProfiler(r.profilerInfo);
            mProfiler.startProfiling();
        }

        // Make sure we are running with the most recent config.
        handleConfigurationChanged(null, null);

        WindowManagerGlobal.initialize();
        //调用performLaunchActivity返回一个Activity [#1.1.1]
        Activity a = performLaunchActivity(r, customIntent);

        if (a != null) {
            r.createdConfig = new Configuration(mConfiguration);
            reportSizeConfigurations(r);
            Bundle oldState = r.state;
            //[#1.1.2]
            handleResumeActivity(r.token, false, r.isForward,
                    !r.activity.mFinished && !r.startsNotResumed, r.lastProcessedSeq, reason);

           ...
        }
        ...
    }

1.1.1 创建Activity

frameworks/base/core/java/android/app/ActivityThread.java

 private Activity performLaunchActivity(ActivityClientRecord r, Intent customIntent) {
        ActivityInfo aInfo = r.activityInfo;
        ...
        ComponentName component = r.intent.getComponent();
        ...
        Activity activity = null;
        try {
            java.lang.ClassLoader cl = r.packageInfo.getClassLoader();
            //通过类名创建Acrivity
            activity = mInstrumentation.newActivity(
                    cl, component.getClassName(), r.intent);
                            StrictMode.incrementExpectedActivityCount(activity.getClass());
            r.intent.setExtrasClassLoader(cl);
            r.intent.prepareToEnterProcess();
            if (r.state != null) {
                r.state.setClassLoader(cl);
            }
        } catch (Exception e) {
           ...
        }

        try {
            Application app = r.packageInfo.makeApplication(false, mInstrumentation);
           ....
            if (activity != null) {
                //创建appContext
                Context appContext = createBaseContextForActivity(r, activity);
               ...
            }
          //下面这个函数会调用Activity的onCreate()函数
                if (r.isPersistable()) {
                    mInstrumentation.callActivityOnCreate(activity, r.state, r.persistentState);
                } else {
                    mInstrumentation.callActivityOnCreate(activity, r.state);
                }
               ...

        return activity;
    }

performLaunchActivity()根据类名以ClassLoader.loadClass(className).newInstance()创建一个Activity,还会调用到ActivityonCreate()函数,在onCreate()中我们会调用setContentView()来设置外观。

1.1.2 分析handleResumeActivity

通过performLaunchActivity()已经创建好了一个Activity并且设置了布局。

final void handleResumeActivity(IBinder token,
            boolean clearHide, boolean isForward, boolean reallyResume, int seq, String reason) {
        ActivityClientRecord r = mActivities.get(token);
        if (!checkAndUpdateLifecycleSeq(seq, r, "resumeActivity")) {
            return;
        }

        // If we are getting ready to gc after going to the background, well
        // we are back active so skip it.
        unscheduleGcIdler();
        mSomeActivitiesChanged = true;

        // TODO Push resumeArgs into the activity for consideration
        r = performResumeActivity(token, clearHide, reason);

        if (r != null) {
            final Activity a = r.activity;

            final int forwardBit = isForward ?
                    WindowManager.LayoutParams.SOFT_INPUT_IS_FORWARD_NAVIGATION : 0;

            // If the window hasn't yet been added to the window manager,
            // and this guy didn't finish itself or start another activity,
            // then go ahead and add the window.
            boolean willBeVisible = !a.mStartedActivity;
            if (!willBeVisible) {
                try {
                    willBeVisible = ActivityManagerNative.getDefault().willActivityBeVisible(
                            a.getActivityToken());
                } catch (RemoteException e) {
                    throw e.rethrowFromSystemServer();
                }
            }
            if (r.window == null && !a.mFinished && willBeVisible) {
                r.window = r.activity.getWindow();
              //获得顶层view []
                View decor = r.window.getDecorView();
                decor.setVisibility(View.INVISIBLE);
              //获得ViewManager []
                ViewManager wm = a.getWindowManager();
                WindowManager.LayoutParams l = r.window.getAttributes();
                a.mDecor = decor;
                l.type = WindowManager.LayoutParams.TYPE_BASE_APPLICATION;
                l.softInputMode |= forwardBit;
                if (r.mPreserveWindow) {
                    a.mWindowAdded = true;
                    r.mPreserveWindow = false;
                    // Normally the ViewRoot sets up callbacks with the Activity
                    // in addView->ViewRootImpl#setView. If we are instead reusing
                    // the decor view we have to notify the view root that the
                    // callbacks may have changed.
                    ViewRootImpl impl = decor.getViewRootImpl();
                    if (impl != null) {
                        impl.notifyChildRebuilt();
                    }
                }
                if (a.mVisibleFromClient && !a.mWindowAdded) {
                    a.mWindowAdded = true;
                    //把decor view对象添加到ViewManager中
                    wm.addView(decor, l);
                }

            // If the window has already been added, but during resume
            // we started another activity, then don't yet make the
            // window visible.
            } else if (!willBeVisible) {
             ...//
            }

    ...//
        }

2 分析setContentView

handleResumeAcivity()中涉及的都是UI相关的,在上一preformLaunchActivity()函数中,说到会调用onCreate()onCreate()中一般都是设置布局。所以要理解handleResumeActivity()需要先看看onCreate()结束,系统为我们提供了什么。

frameworks/base/core/java/android/app/Activity.java

public void setContentView(View view) {
        getWindow().setContentView(view);
        initWindowDecorActionBar();
    }

这个函数有很多重载,但是都是最终都是调用getWindow().setContentView(),getWindow返回的是一个Activity中的Window对象,Window是一个抽象类。

Window:

Abstract base class for a top-level window look and behavior policy. An instance of this class should be used as the top-level view added to the window manager. It provides standard UI policies such as a background, title area, default key processing, etc.

The only existing implementation of this abstract class is android.view.PhoneWindow, which you should instantiate when needing a Window.

window是一个抽象的基类,控制顶层窗口的外观和行为策略。提供背景啊,标题,按键处理等。同时也提到唯一的实现类是PhoneWindow,getWindow()返回的应该就是一个PhoneWindow实例。

另一个和UI有关的就是View:

This class represents the basic building block for user interface components. A View occupies a rectangular area on the screen and is responsible for drawing and event handling. View is the base class for widgets, which are used to create interactive UI components (buttons, text fields, etc.). TheViewGroup subclass is the base class for layouts, which are invisible containers that hold other Views (or other ViewGroups) and define their layout properties.

View代表了一个基本的UI待援,一个View占据一个块矩形的区域,负责绘制和处理事件...

2.1 Activity的Window

performLaunchActivity()中,创建了Activity对象后,接着又执行了Activity.attch()

frameworks/base/core/java/android/app/Activity.java

 final void attach(Context context, ActivityThread aThread,
            Instrumentation instr, IBinder token, int ident,
            Application application, Intent intent, ActivityInfo info,
            CharSequence title, Activity parent, String id,
            NonConfigurationInstances lastNonConfigurationInstances,
            Configuration config, String referrer, IVoiceInteractor voiceInteractor,
            Window window) {
        attachBaseContext(context);

        mFragments.attachHost(null /*parent*/);
        //真正的Window
        mWindow = new PhoneWindow(this, window);
        mWindow.setWindowControllerCallback(this);
        mWindow.setCallback(this);
        mWindow.setOnWindowDismissedCallback(this);
        mWindow.getLayoutInflater().setPrivateFactory(this);
        if (info.softInputMode != WindowManager.LayoutParams.SOFT_INPUT_STATE_UNSPECIFIED) {
            mWindow.setSoftInputMode(info.softInputMode);
        }
        if (info.uiOptions != 0) {
            mWindow.setUiOptions(info.uiOptions);
        }
        mUiThread = Thread.currentThread();

        mMainThread = aThread;
        mInstrumentation = instr;
        mToken = token;
        mIdent = ident;
        mApplication = application;
        mIntent = intent;
        mReferrer = referrer;
        mComponent = intent.getComponent();
        mActivityInfo = info;
        mTitle = title;
        mParent = parent;
        mEmbeddedID = id;
        mLastNonConfigurationInstances = lastNonConfigurationInstances;
        if (voiceInteractor != null) {
            if (lastNonConfigurationInstances != null) {
                mVoiceInteractor = lastNonConfigurationInstances.voiceInteractor;
            } else {
                mVoiceInteractor = new VoiceInteractor(voiceInteractor, this, this,
                        Looper.myLooper());
            }
        }
    //设置windowManager
        mWindow.setWindowManager(
                (WindowManager)context.getSystemService(Context.WINDOW_SERVICE),
                mToken, mComponent.flattenToString(),
                (info.flags & ActivityInfo.FLAG_HARDWARE_ACCELERATED) != 0);
        if (mParent != null) {
            mWindow.setContainer(mParent.getWindow());
        }
   //保存windowManager
        mWindowManager = mWindow.getWindowManager();
        mCurrentConfig = config;
    }

通过上面代码就可以明确知道Activity中的mWindowPhoneWindow实例,那么mWindowManager又是谁?

2.2 真正的windowManager

frameworks/base/core/java/android/view/Window.java

public void setWindowManager(WindowManager wm, IBinder appToken, String appName,
            boolean hardwareAccelerated) {
        mAppToken = appToken;
        mAppName = appName;
        mHardwareAccelerated = hardwareAccelerated
                || SystemProperties.getBoolean(PROPERTY_HARDWARE_UI, false);
        if (wm == null) {
            wm = (WindowManager)mContext.getSystemService(Context.WINDOW_SERVICE);
        }
  //这里调用了windowManagerImpl的方法创建windowmanager
        mWindowManager = ((WindowManagerImpl)wm).createLocalWindowManager(this);
    }

frameworks/base/core/java/android/view/WindowManagerImpl.java

 public WindowManagerImpl createLocalWindowManager(Window parentWindow) {
        return new WindowManagerImpl(mContext, parentWindow);
    }

翻了一个WindowManagerImpl实例对象。所以mWindow中设置和activity中保存的mWindowManagerWindowManagerImpl

2.3 setContentView总结

通过上面分析,setContentView()最终交给了PhoneWindow.setContentView()

frameworks/base/core/java/com/android/internal/policy/PhoneWindow.java

public void setContentView(View view, ViewGroup.LayoutParams params) {
        // Note: FEATURE_CONTENT_TRANSITIONS may be set in the process of installing the window
        // decor, when theme attributes and the like are crystalized. Do not check the feature
        // before this happens.
        if (mContentParent == null) {
          //
            installDecor();
        } else if (!hasFeature(FEATURE_CONTENT_TRANSITIONS)) {
            mContentParent.removeAllViews();
        }
        //支持转场动画
        if (hasFeature(FEATURE_CONTENT_TRANSITIONS)) {
            view.setLayoutParams(params);
            final Scene newScene = new Scene(mContentParent, view);
            transitionTo(newScene);
        } else {
            mContentParent.addView(view, params);
        }
        mContentParent.requestApplyInsets();
        final Callback cb = getCallback();
        if (cb != null && !isDestroyed()) {
            cb.onContentChanged();
        }
        mContentParentExplicitlySet = true;
    }

FEATURE_CONTENT_TRANSITIONS这是L后添加的转场动画功能。通过mContentParent.addView()把我设置的不去添加到decor中的mCtentParent中。

3 重回handleResumeActivity

frameworks/base/core/java/android/app/ActivityThread.java

final void handleResumeActivity(IBinder token,
            boolean clearHide, boolean isForward, boolean reallyResume, int seq, String reason) {
        ActivityClientRecord r = mActivities.get(token);
       ...//
        r = performResumeActivity(token, clearHide, reason);

        if (r != null) {
            final Activity a = r.activity;

            final int forwardBit = isForward ?
                    WindowManager.LayoutParams.SOFT_INPUT_IS_FORWARD_NAVIGATION : 0;

            // If the window hasn't yet been added to the window manager,
            // and this guy didn't finish itself or start another activity,
            // then go ahead and add the window.
            boolean willBeVisible = !a.mStartedActivity;
            if (!willBeVisible) {
                try {
                    willBeVisible = ActivityManagerNative.getDefault().willActivityBeVisible(
                            a.getActivityToken());
                } catch (RemoteException e) {
                    throw e.rethrowFromSystemServer();
                }
            }
            if (r.window == null && !a.mFinished && willBeVisible) {
                r.window = r.activity.getWindow();
              //获得顶层view []
                View decor = r.window.getDecorView();
                decor.setVisibility(View.INVISIBLE);
              //获得ViewManager []
                ViewManager wm = a.getWindowManager();
                WindowManager.LayoutParams l = r.window.getAttributes();
                a.mDecor = decor;
                l.type = WindowManager.LayoutParams.TYPE_BASE_APPLICATION;
                l.softInputMode |= forwardBit;
                if (r.mPreserveWindow) {
                    a.mWindowAdded = true;
                    r.mPreserveWindow = false;
                    // Normally the ViewRoot sets up callbacks with the Activity
                    // in addView->ViewRootImpl#setView. If we are instead reusing
                    // the decor view we have to notify the view root that the
                    // callbacks may have changed.
                    ViewRootImpl impl = decor.getViewRootImpl();
                    if (impl != null) {
                        impl.notifyChildRebuilt();
                    }
                }
                if (a.mVisibleFromClient && !a.mWindowAdded) {
                    a.mWindowAdded = true;
                    //把decor view对象添加到ViewManager中
                    wm.addView(decor, l);
                }
         ...//
        }

通过分析setContentView()知道了这里的decor,wm的内容,wm就是windowManagerImpl的实例,通过调用addView()decor添加

frameworks/base/core/java/android/view/WindowManagerImpl.java

public void addView(@NonNull View view, @NonNull ViewGroup.LayoutParams params) {
        applyDefaultToken(params);
        mGlobal.addView(view, params, mContext.getDisplay(), mParentWindow);
    }

然后并没有做什么不得了的事,仅是调用了另外的对象。mGlobal看名称就知道全局对象。

frameworks/base/core/java/android/view/WindowManagerGlobal.java

 public void addView(View view, ViewGroup.LayoutParams params,
            Display display, Window parentWindow) {
        
        final WindowManager.LayoutParams wparams = (WindowManager.LayoutParams) params;
        if (parentWindow != null) {
            parentWindow.adjustLayoutParamsForSubWindow(wparams);
        } else {
          //设置硬件加速
            // If there's no parent, then hardware acceleration for this view is
            // set from the application's hardware acceleration setting.
            final Context context = view.getContext();
            if (context != null
                    && (context.getApplicationInfo().flags
                            & ApplicationInfo.FLAG_HARDWARE_ACCELERATED) != 0) {
                wparams.flags |= WindowManager.LayoutParams.FLAG_HARDWARE_ACCELERATED;
            }
        }

        ViewRootImpl root;
        View panelParentView = null;

        synchronized (mLock) {
            // Start watching for system property changes.
            ...//

            int index = findViewLocked(view, false);
            if (index >= 0) {
                if (mDyingViews.contains(view)) {
                    // Don't wait for MSG_DIE to make it's way through root's queue.
                    mRoots.get(index).doDie();
                } else {
                   ....//
                }
                // The previous removeView() had not completed executing. Now it has.
            }

            // If this is a panel window, then find the window it is being
            // attached to for future reference.
            if (wparams.type >= WindowManager.LayoutParams.FIRST_SUB_WINDOW &&
                    wparams.type <= WindowManager.LayoutParams.LAST_SUB_WINDOW) {
                final int count = mViews.size();
                for (int i = 0; i < count; i++) {
                    if (mRoots.get(i).mWindow.asBinder() == wparams.token) {
                        panelParentView = mViews.get(i);
                    }
                }
            }
            //创建一个viewRootImpl
            root = new ViewRootImpl(view.getContext(), display);

            view.setLayoutParams(wparams);
            //保存这写对象
            mViews.add(view);
            mRoots.add(root);
            mParams.add(wparams);

            // do this last because it fires off messages to start doing things
            try {
                //把decorView设置到viewRootImpl中
                root.setView(view, wparams, panelParentView);
            } catch (RuntimeException e) {
           ...//
            }
        }
    }

有引出了行的对象ViewRootImpl,还实现了ViewParent

3.1 ViewRootImpl

查看ViewRootImpl源码可以发现里面有一个Surface字段,和一个W类型的字段,W是其内部类,集成至IWindow.Stub,很熟悉,binder接口就喜欢Ixxx。同时还保存存了decorView

3.1.1 ViewRootImpl的创建

frameworks/base/core/java/android/view/ViewRootImpl.java

public ViewRootImpl(Context context, Display display) {
        mContext = context;
        //得到一个会话
        mWindowSession = WindowManagerGlobal.getWindowSession();
        ..//
          //创建一个W对象
        mWindow = new W(this);
        ..//
        mAttachInfo = new View.AttachInfo(mWindowSession, mWindow, display, this, mHandler, this);
       ...//
        mViewConfiguration = ViewConfiguration.get(context);
        mDensity = context.getResources().getDisplayMetrics().densityDpi;
        mNoncompatDensity = context.getResources().getDisplayMetrics().noncompatDensityDpi;
        mFallbackEventHandler = new PhoneFallbackEventHandler(context);
        mChoreographer = Choreographer.getInstance();
        mDisplayManager = (DisplayManager)context.getSystemService(Context.DISPLAY_SERVICE);
        loadSystemProperties();
    }

3.1.1.1 getWindowSesseion()

session我们在Binder系统中还是比较常见,表示客户端与服务端的一次会话。

frameworks/base/core/java/android/view/WindowManagerGlobal.java

 public static IWindowSession getWindowSession() {
        synchronized (WindowManagerGlobal.class) {
            if (sWindowSession == null) {
                try {
                    InputMethodManager imm = InputMethodManager.getInstance();
                    //获得WindowManagerService的binder代理
                    IWindowManager windowManager = getWindowManagerService();
                  //binder调用获得session
                    sWindowSession = windowManager.openSession(
                            new IWindowSessionCallback.Stub() {
                                @Override
                                public void onAnimatorScaleChanged(float scale) {
                                    ValueAnimator.setDurationScale(scale);
                                }
                            },
                            imm.getClient(), imm.getInputContext());
                } catch (RemoteException e) {
                    throw e.rethrowFromSystemServer();
                }
            }
            return sWindowSession;
        }
    }

3.1.1.2 ViewRootImpl.setView()

源码中可见,先调用了requestLayout,然后又调用了IWindowsSessionadd()函数,向WindowManagerService发消息。

源码真的太多了...发一个Handler消息绕了好多方法

3.2 Surface

View是UI的单元,绘制工作都是在onDraw函数中完成。而Surface就相当于画布。

Surface:

Handle onto a raw buffer that is being managed by the screen compositor.

Surface操作一块Raw buffer,Screen compositor管理这块Raw buffer

4 ViewRootImpl与WindowManagerService

关系图

ViewRootImpl通过IWindowSessionWindowManager进程进行跨进程交互,也就是binder调用。

ViewRootImpl内部有一个W类型的对象,它也是基于binder通信,但是它是客户端,处理WindowManagerService的请求。例如按键,触屏事件的通知:

WindowManagerService接收到事件,WindowManagerService找到UI位于顶层的进程所对应的IWindow对象,这是一个Bp端,调用这个IWindow对象的dispatchxxx(),ViewRootImplBnIWindow接受事件,再派发给相应的处理者。

5 Activity的UI绘制

前面提到,ViewRootImplsetView()函数会调用会有一个requestLayout(),它的调用栈很深:

requestLayout --> scheduleTraversals() -- > mChoreographer.postCallback(Choreographer.CALLBACK_TRAVERSAL, mTraversalRunnable, null) --> doTraversal() --> performTraversals()

按照原书中所说的在performTraversals()找到两个重要的函数:

relayoutWindow()draw(),在7.0中显现调用了performDraw(),在performDraw()中调用的draw()方法。

relayoutWindow()函数中调用了WindowSessionlayout()函数。

draw()方中做了比较多的计算,真正的绘制放在了drawSoftware()

frameworks/base/core/java/android/view/ViewRootImpl.java

private boolean drawSoftware(Surface surface, AttachInfo attachInfo, int xoff, int yoff,
            boolean scalingRequired, Rect dirty) {

        // Draw with software renderer.
        final Canvas canvas;
        try {
            final int left = dirty.left;
            final int top = dirty.top;
            final int right = dirty.right;
            final int bottom = dirty.bottom;

            canvas = mSurface.lockCanvas(dirty);

            // The dirty rectangle can be modified by Surface.lockCanvas()
            //noinspection ConstantConditions
            if (left != dirty.left || top != dirty.top || right != dirty.right
                    || bottom != dirty.bottom) {
                attachInfo.mIgnoreDirtyState = true;
            }

            // TODO: Do this in native
            canvas.setDensity(mDensity);
        } catch (Surface.OutOfResourcesException e) {
            handleOutOfResourcesException(e);
            return false;
        } catch (IllegalArgumentException e) {
           ...//
        }
  ..//

            // If this bitmap's format includes an alpha channel, we
            // need to clear it before drawing so that the child will
            // properly re-composite its drawing on a transparent
            // background. This automatically respects the clip/dirty region
            // or
            // If we are applying an offset, we need to clear the area
            // where the offset doesn't appear to avoid having garbage
            // left in the blank areas.
            if (!canvas.isOpaque() || yoff != 0 || xoff != 0) {
                canvas.drawColor(0, PorterDuff.Mode.CLEAR);
            }

            dirty.setEmpty();
            mIsAnimating = false;
            mView.mPrivateFlags |= View.PFLAG_DRAWN;
            try {
                canvas.translate(-xoff, -yoff);
                if (mTranslator != null) {
                    mTranslator.translateCanvas(canvas);
                }
                canvas.setScreenDensity(scalingRequired ? mNoncompatDensity : 0);
                attachInfo.mSetIgnoreDirtyState = false;

                mView.draw(canvas);

                drawAccessibilityFocusedDrawableIfNeeded(canvas);
            } finally {
                if (!attachInfo.mSetIgnoreDirtyState) {
                    // Only clear the flag if it was not set during the mView.draw() call
                    attachInfo.mIgnoreDirtyState = false;
                }
            }
        } finally {
            try {
                surface.unlockCanvasAndPost(canvas);
            } catch (IllegalArgumentException e) {
              ...//
            }
        
        }
        return true;
    }

6 总结

  • Activity的顶层ViewDecorView,通过在ActivityonCreate()调用setContentView()设置的View只是DecorView种的一部分,成为contentView,DecorView是一个FrameLayout类型的ViewGroup
  • Activity和UI有关,他包含一个Window(实际实例为PhoneWindow)和一个WindowManager(实际实例为WindowManagerImpl)。这两者控制Activity的显示、
  • WindowManagerImpl中有一个ViewRootImpl对象,它实现了ViewParent接口,它有两个重要成员,一个是DecorView一个是SurfaceViewRootImpl还通过BinderWindowManagerService交互,涉及两个类IWindowSessionIWindow
  • Activity的绘制是从Surface所处一块Canvas开始的,最后释放这个Canvas.
最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 159,015评论 4 362
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 67,262评论 1 292
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 108,727评论 0 243
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 43,986评论 0 205
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 52,363评论 3 287
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 40,610评论 1 219
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 31,871评论 2 312
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 30,582评论 0 198
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 34,297评论 1 242
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 30,551评论 2 246
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 32,053评论 1 260
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 28,385评论 2 253
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 33,035评论 3 236
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 26,079评论 0 8
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 26,841评论 0 195
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 35,648评论 2 274
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 35,550评论 2 270

推荐阅读更多精彩内容