Activity中View创建到添加在Window窗口上到显示的过程源码分析

前言

分析WindowManager之前先了解一下 Window
Window也就是窗口,它是一个抽象类,其具体实现类是PhoneWindow。

Window有三种类型:应用Window,子Window以及系统Window

Window 应用 层级
应用Window Activity 1~99
子Window Dialog 1000~1999
系统Window 系统状态栏、Toast 2000~2999

这些层级对应WindowManager.LayoutParams的type参数,

Window和WindowManger

WindowManager顾名思义指的是Window的管理者,Window展示的内容是由View承载的也就是DecorView,创建一个Window需要通过WindowManager来协助完成。而WindowManager的具体实现类是WindowManagerImpl。
WindowManager是外界访问Window的入口,它是一个接口并继承ViewManager接口

public interface ViewManager
{
    //添加View
    public void addView(View view, ViewGroup.LayoutParams params);
    //更新View
    public void updateViewLayout(View view, ViewGroup.LayoutParams params);
    //删除View
    public void removeView(View view);
}

//查看WindowManger的具体实现类WindowManagerImpl源码
@Override
public void addView(@NonNull View view, @NonNull ViewGroup.LayoutParams params) {
        applyDefaultToken(params);
        //mParentWindow为一个Window对象
        mGlobal.addView(view, params, mContext.getDisplay(), mParentWindow);
}

@Override
public void updateViewLayout(@NonNull View view, @NonNull ViewGroup.LayoutParams params) {
        applyDefaultToken(params);
        mGlobal.updateViewLayout(view, params);
}

@Override
public void removeView(View view) {
        mGlobal.removeView(view, false);
}

查看WindowManagerImpl源码发现addView方法实际是调用mGlobal的addView方法
而mGlobal是一个WindowManagerGlobal单例对象,也就是说WindowManagerGlobal实现了WindowManagerImpl的功能
查看WindowMangerGlobal.java$addView方法核心源码

 public void addView(View view, ViewGroup.LayoutParams params,
            Display display, Window parentWindow) {

        ......//代码省略
        //参数类型转换
        final WindowManager.LayoutParams wparams = (WindowManager.LayoutParams) params;
       
        ......//代码省略
        
        // ViewRootImpl是顶级View的管理者是final类,其实现ViewParent接口,ViewParent是处理视图 与父级交互的API
        ViewRootImpl root;
        View panelParentView = null;
        synchronized (mLock) {
            ......//代码省略

            root = new ViewRootImpl(view.getContext(), display);

            view.setLayoutParams(wparams);

            // private final ArrayList<View> mViews = new ArrayList<View>();
            // private final ArrayList<ViewRootImpl> mRoots = new ArrayList<ViewRootImpl>();
            // private final ArrayList<WindowManager.LayoutParams> mParams = new ArrayList<WindowManager.LayoutParams>();
            mViews.add(view);
            mRoots.add(root);
            mParams.add(wparams);
        }

        try {
            //添加视图View
            root.setView(view, wparams, panelParentView);
        } catch (RuntimeException e) {
            //运行异常则移除View
            synchronized (mLock) {
                final int index = findViewLocked(view, false);
                if (index >= 0) {
                    removeViewLocked(index, true);
                }
            }
            throw e;
        }
    }

查看ViewRootImpl.java$setView方法核心源码

   public void setView(View view, WindowManager.LayoutParams attrs, View panelParentView) {
        synchronized (this) {
            if (mView == null) {
                mView = view;
                int res; /* = WindowManagerImpl.ADD_OKAY; */

                //在添加到窗口之前设置布局,确保在接收到系统中的任何其他事件之前执行重新布局
                requestLayout();
               //mWindowSession为IWindowSession,它是一个Binder对象,真正的实现类是Session,内部实现IPC通信
               //mWindow是一个W对象,W为ViewRootImpl静态内部类继承IWindow.Stub
               res = mWindowSession.addToDisplay(mWindow, mSeq, mWindowAttributes,
                     getHostVisibility(), mDisplay.getDisplayId(),
                     mAttachInfo.mContentInsets, mAttachInfo.mStableInsets,
                            mAttachInfo.mOutsets, mInputChannel);
            
            }
        }
    }

    //查看Session.java$addToDisplay方法源码
    @Override
    public int addToDisplay(IWindow window, int seq, WindowManager.LayoutParams attrs,
            int viewVisibility, int displayId, Rect outContentInsets, Rect outStableInsets,
            Rect outOutsets, InputChannel outInputChannel) {
        //mService 为WindowMangerService对象,其实质是调用了WMS的addWindow方法(WMS分析待续 )
        return mService.addWindow(this, window, seq, attrs, viewVisibility, displayId,
                outContentInsets, outStableInsets, outOutsets, outInputChannel);
    }

分析完WindowManager的addView方法后,可以知道Window的添加过程:


Window添加过程.png

Activity添加View的过程分析

Activity添加View的过程也就是把View添加到Window上的过程,添加View方法需要从setContentView开始
查看Activity.java$setContentView源码

  public void setContentView(@LayoutRes int layoutResID) {
        //getWindow返回的是一个Window对象,Window的具体实现类为PhoneWindow下面分析其setContentView方法
        getWindow().setContentView(layoutResID);
        //初始化ActionBar
        initWindowDecorActionBar();
    }
    //查看PhoneWindow.java$setContentView方法源码
   @Override
    public void setContentView(int layoutResID) {
       
        // mContentParent为ViewGroup对象,若其为空则初始化DecorView,
        if (mContentParent == null) {
            //安装DecorView
            installDecor();
        //mContentParent已经加载过且不需要动画移除所有View
        } else if (!hasFeature(FEATURE_CONTENT_TRANSITIONS)) {
            mContentParent.removeAllViews();
        }
        //若设置FEATURE_CONTENT_TRANSITIONS(过渡动画)则添加Scene来过渡启动
        if (hasFeature(FEATURE_CONTENT_TRANSITIONS)) {
            final Scene newScene = Scene.getSceneForLayout(mContentParent, layoutResID,
                    getContext());
            transitionTo(newScene);
        } else {
            //通过添加资源文件把View添加到mContentParent中,后面会给出分析
            mLayoutInflater.inflate(layoutResID, mContentParent);
        }
        mContentParent.requestApplyInsets();
        final Callback cb = getCallback();
        if (cb != null && !isDestroyed()) {
            cb.onContentChanged();
        }
        mContentParentExplicitlySet = true;
    }

 //继续查看installDecor方法
 private void installDecor() {
        mForceDecorInstall = false;
        if (mDecor == null) {
            //生成DecorView
            mDecor = generateDecor(-1);
            mDecor.setDescendantFocusability(ViewGroup.FOCUS_AFTER_DESCENDANTS);
            mDecor.setIsRootNamespace(true);
            if (!mInvalidatePanelMenuPosted && mInvalidatePanelMenuFeatures != 0) {
                mDecor.postOnAnimation(mInvalidatePanelMenuRunnable);
            }
        } else {
            //mDecor设置Window窗口
            mDecor.setWindow(this);
        }
        if (mContentParent == null) {
            //生成mContentParent对象
            mContentParent = generateLayout(mDecor);

            // 设置UI在适当的情况下忽略合适的系统窗口
            mDecor.makeOptionalFitsSystemWindows();
            } else {
                mTitleView = (TextView) findViewById(R.id.title);
                //设置title
                if (mTitleView != null) {
                    if ((getLocalFeatures() & (1 << FEATURE_NO_TITLE)) != 0) {
                        final View titleContainer = findViewById(R.id.title_container);
                        if (titleContainer != null) {
                            titleContainer.setVisibility(View.GONE);
                        } else {
                            mTitleView.setVisibility(View.GONE);
                        }
                        mContentParent.setForeground(null);
                    } else {
                        mTitleView.setText(mTitle);
                    }
                }
            }

            if (mDecor.getBackground() == null && mBackgroundFallbackResource != 0) {
                //设置DecorView背景
                mDecor.setBackgroundFallback(mBackgroundFallbackResource);
            }
            ......//代码省略
            }
        }
    }

查看PhoneWindow.java$generateDecor方法核心源码

 protected DecorView generateDecor(int featureId) {
        ......//代码省略
        //通过直接创建DecorView对象并返回,DecorView继承FrameLayout
        return new DecorView(context, featureId, this, getAttributes());
    }

查看PhoneWindow.java$generatelayout方法核心源码

protected ViewGroup generateLayout(DecorView decor) {
         //获取当前主题并设置其属性
        TypedArray a = getWindowStyle();

        ......//省略其设置属性代码

        ......//省略其资源加载代码
     

        //通过用户设置的Feature来创建相应的布局主题。
        //这也是在代码中设置主题或者requestFeature的时候必须要在setContentView之前的原因
        int layoutResource;
        int features = getLocalFeatures();
        // System.out.println("Features: 0x" + Integer.toHexString(features));
        if ((features & (1 << FEATURE_SWIPE_TO_DISMISS)) != 0) {
            layoutResource = R.layout.screen_swipe_dismiss;
        } else if ((features & ((1 << FEATURE_LEFT_ICON) | (1 << FEATURE_RIGHT_ICON))) != 0) {
            if (mIsFloating) {
                TypedValue res = new TypedValue();
                getContext().getTheme().resolveAttribute(
                        R.attr.dialogTitleIconsDecorLayout, res, true);
                layoutResource = res.resourceId;
            } else {
                layoutResource = R.layout.screen_title_icons;
            }
            // XXX Remove this once action bar supports these features.
            removeFeature(FEATURE_ACTION_BAR);
            // System.out.println("Title Icons!");
        } else if ((features & ((1 << FEATURE_PROGRESS) | (1 << FEATURE_INDETERMINATE_PROGRESS))) != 0
                && (features & (1 << FEATURE_ACTION_BAR)) == 0) {

           ......//根绝Feature设置布局文件
        }

        mDecor.startChanging();
        mDecor.onResourcesLoaded(mLayoutInflater, layoutResource);

        //DecorView是顶级View,它包括状态栏,导航栏,内容区等,而contentParent指的是屏幕显示的内容区,
        //通常我们设置的布局文件则是contentParent中的子元素
        ViewGroup contentParent = (ViewGroup)findViewById(ID_ANDROID_CONTENT);
        if (contentParent == null) {
            throw new RuntimeException("Window couldn't find content container view");
        }

        if ((features & (1 << FEATURE_INDETERMINATE_PROGRESS)) != 0) {
            ProgressBar progress = getCircularProgressBar(false);
            if (progress != null) {
                progress.setIndeterminate(true);
            }
        }

        if ((features & (1 << FEATURE_SWIPE_TO_DISMISS)) != 0) {
            registerSwipeCallbacks();
        }

        // Remaining setup -- of background and title -- that only applies
        // to top-level windows.
        if (getContainer() == null) {
            final Drawable background;
            if (mBackgroundResource != 0) {
                background = getContext().getDrawable(mBackgroundResource);
            } else {
                background = mBackgroundDrawable;
            }
            //给DecorView设置窗口背景色
            mDecor.setWindowBackground(background);

            ......//DecorView属性设置

         }

        mDecor.finishChanging();

        return contentParent;
    }

在查看LayoutInflate.java$inflate方法源码之前先看下LayoutInflate类,其为抽象类,该类的作用是,将xml布局文件实例化为相应的View对象,在PhoneWindow类中在其构造函数中通过调用layoutInflate的from方法创建LayoutInflate对象,查看LayoutInflate.java$from方法源码

   public static LayoutInflater from(Context context) {
        //通过getSystemService函数使用Context.LAYOUT_INFLATER_SERVICE标志位转换成LayoutInflate服务对象
        LayoutInflater LayoutInflater =
                (LayoutInflater) context.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
        if (LayoutInflater == null) {
            throw new AssertionError("LayoutInflater not found.");
        }
        return LayoutInflater;
    }

   //接下来查看LayoutInflate.java$inflate方法源码
   public View inflate(@LayoutRes int resource, @Nullable ViewGroup root) {
          return inflate(resource, root, root != null);
    }

   public View inflate(@LayoutRes int resource, @Nullable ViewGroup root, boolean attachToRoot) {
        final Resources res = getContext().getResources();
        if (DEBUG) {
            Log.d(TAG, "INFLATING from resource: \"" + res.getResourceName(resource) + "\" ("
                    + Integer.toHexString(resource) + ")");
        }
        //XmlResourceParser为xml资源解析器是一个接口其继承XmlPullParser,AttributeSet,AutoCloseable
        final XmlResourceParser parser = res.getLayout(resource);
        try {
            return inflate(parser, root, attachToRoot);
        } finally {
            parser.close();
        }
    }

查看LayoutI.java$inflate方法源码

 public View inflate(XmlPullParser parser, @Nullable ViewGroup root, boolean attachToRoot) {
        synchronized (mConstructorArgs) {
            Trace.traceBegin(Trace.TRACE_TAG_VIEW, "inflate");

            final Context inflaterContext = mContext;
            //获取xml的属性集合
            final AttributeSet attrs = Xml.asAttributeSet(parser);
            Context lastContext = (Context) mConstructorArgs[0];
            mConstructorArgs[0] = inflaterContext;
            View result = root;

            try {
                ......//查找根节点
            
                final String name = parser.getName();

                if (TAG_MERGE.equals(name)) {
                    if (root == null || !attachToRoot) {
                        throw new InflateException("<merge /> can be used only with a valid "
                                + "ViewGroup root and attachToRoot=true");
                    }
                    //如果xml布局文件中使用到merge标签将执行这一步
                    rInflate(parser, root, inflaterContext, attrs, false);
                } else {
                    //使用提供的属性集从标签名称创建临时根视图
                    final View temp = createViewFromTag(root, name, inflaterContext, attrs);

                    ViewGroup.LayoutParams params = null;

                    if (root != null) {
                        if (DEBUG) {
                            System.out.println("Creating params from root: " +
                                    root);
                        }
                        //创建与root相匹配的布局参数(如果提供)
                        params = root.generateLayoutParams(attrs);
                        if (!attachToRoot) {
                            //如果没有与root有联系则为 temp设置布局参数,如果有联系将会使用下面的addView
                            temp.setLayoutParams(params);
                        }
                    }

                    if (DEBUG) {
                        System.out.println("-----> start inflating children");
                    }

                    // 该方法调用的是rInflate方法最终使用递归方法用于降低xml层次结构并实例化子视图
                    rInflateChildren(parser, temp, attrs, true);

                    if (DEBUG) {
                        System.out.println("-----> done inflating children");
                    }

                    // We are supposed to attach all the views we found (int temp)
                    // to root. Do that now.
                    if (root != null && attachToRoot) {
                        root.addView(temp, params);
                    }

                    // Decide whether to return the root that was passed in or the
                    // top view found in xml.
                    if (root == null || !attachToRoot) {
                        result = temp;
                    }
                }

            } catch (XmlPullParserException e) {
                final InflateException ie = new InflateException(e.getMessage(), e);
                ie.setStackTrace(EMPTY_STACK_TRACE);
                throw ie;
            } catch (Exception e) {
                final InflateException ie = new InflateException(parser.getPositionDescription()
                        + ": " + e.getMessage(), e);
                ie.setStackTrace(EMPTY_STACK_TRACE);
                throw ie;
            } finally {
                //清除在上下文中存留的静态引用
                mConstructorArgs[0] = lastContext;
                mConstructorArgs[1] = null;

                Trace.traceEnd(Trace.TRACE_TAG_VIEW);
            }

            return result;
        }
    }

上面分析了Activity调用setContentView方法,该方法的作用为创建DecorView和加载Xml布局。
问题来了上面只说了视图View的创建与加载,那么View是在哪一步添加到Window中呢,又在哪一步显示呢?
这就涉及到Activity的启动过程,如果不了解Activity启动过程的话可以去了解一下。

在分析Activity启动过程中可以了解到Activity的Window创建是发生在attach方法中,系统会创建Activity所属的Window对象并为其设置回调接口其源码为

 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);
        
        //设置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());
        }
        mWindowManager = mWindow.getWindowManager();
        mCurrentConfig = config;
    }

这里我们直接从ActivityThread.java$handleLaunchActivity方法分析,下面只分析与View添加到Window中有关代码

private void handleLaunchActivity(ActivityClientRecord r, Intent customIntent, String reason) {

 Activity a = performLaunchActivity(r, customIntent);

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

  }

 //继续查看ActivityThread.java$performLaunchActivity方法核心源码
 private Activity performLaunchActivity(ActivityClientRecord r, Intent customIntent) {
      
        Activity activity = null;
        //创建Activity实例
        activity = mInstrumentation.newActivity(
               cl, component.getClassName(), r.intent);

        //调用Activity的attach方法上面分析过该方法中创建Window并初始化
        activity.attach(appContext, this, getInstrumentation(), r.token,
                        r.ident, app, r.intent, r.activityInfo, title, r.parent,
                        r.embeddedID, r.lastNonConfigurationInstances, config,
                        r.referrer, r.voiceInteractor, window);

       
        return activity;
    }
//继续查看handleResumeActivity方法源码
  final void handleResumeActivity(IBinder token,
            boolean clearHide, boolean isForward, boolean reallyResume, int seq, String reason) {
        //根据token获取对应Activity的记录信息
        ActivityClientRecord r = mActivities.get(token);

        ......//代码省略

        //执行onResume方法
        r = performResumeActivity(token, clearHide, reason);

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

            ......//代码省略

          
            if (r.window == null && !a.mFinished && willBeVisible) {
                r.window = r.activity.getWindow();
                //获取DecorView对象
                View decor = r.window.getDecorView();
                //将其设置为不可见状态
                decor.setVisibility(View.INVISIBLE);
                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;
                    
                    ViewRootImpl impl = decor.getViewRootImpl();
                    if (impl != null) {
                        impl.notifyChildRebuilt();
                    }
                }

                if (a.mVisibleFromClient && !a.mWindowAdded) {
                    a.mWindowAdded = true;
                    //执行到这一步才真正和我们上面分析的WindowManager.addView相连,
                    //即把View通过WindowManager添加到Window上
                    wm.addView(decor, l);
                }
            //如果窗口已添加,但在恢复期间,我们开始了另一个活动,但窗口尚未可见。
            } else if (!willBeVisible) {
                if (localLOGV) Slog.v(
                    TAG, "Launch " + r + " mStartedActivity set");
                r.hideForNow = true;
            }

            // Get rid of anything left hanging around.
            cleanUpPendingRemoveWindows(r, false /* force */);

            // The window is now visible if it has been added, we are not
            // simply finishing, and we are not starting another activity.
            if (!r.activity.mFinished && willBeVisible
                    && r.activity.mDecor != null && !r.hideForNow) {
              
                ......//代码省略

                WindowManager.LayoutParams l = r.window.getAttributes();
                if ((l.softInputMode
                        & WindowManager.LayoutParams.SOFT_INPUT_IS_FORWARD_NAVIGATION)
                        != forwardBit) {
                    l.softInputMode = (l.softInputMode
                            & (~WindowManager.LayoutParams.SOFT_INPUT_IS_FORWARD_NAVIGATION))
                            | forwardBit;
                    if (r.activity.mVisibleFromClient) {
                        ViewManager wm = a.getWindowManager();
                        View decor = r.window.getDecorView();
                        //经过一系列判断更新View视图
                        wm.updateViewLayout(decor, l);
                    }
                }
                r.activity.mVisibleFromServer = true;
                mNumVisibleActivities++;
                if (r.activity.mVisibleFromClient) {
                    //调用Activity的makeVisible方法使视图View可见
                    r.activity.makeVisible();
                }
            }

           ......//代码省略
    }

在未执行Activity的makeVisibale方法之前View是不可见状态,也就是在Activity的生命周期的onResume方法中View才可见。
查看Activity.java$makeVisible方法源码

  void makeVisible() {
        //若View已经未添加到Window上则执行该代码块
        if (!mWindowAdded) {
            ViewManager wm = getWindowManager();
            wm.addView(mDecor, getWindow().getAttributes());
            mWindowAdded = true;
        }
        //设置视图View可见
        mDecor.setVisibility(View.VISIBLE);
    }

到这里就把Activity从创建视图View -> 视图View添加到Window窗口中 -> 显示View的过程分析完了。

即:View的在Activity中的创建、添加、显示过程。

总结

  1. Activity在onCreate方法之前调用attach方法,该方法中创建window对象。
  2. 用户在onCreate方法中调用setContentView方法,在该方法中检查DecorView是否存在,若不存在则创建DecorView对象,然后通过LayoutInflate的inflate方法把布局文件实例化为相应的View对象添加到DecorView中。setContentView的作用就是完成View的创建与加载。
  3. 在Activity生命周期onResume方法执行完后,把View添加到Window中去调用的是WindowManager的addView方法,最后调用ViewRootImpl的setView方法把View 添加到Window上,最终调用WindowManagerService的addWindow方法添加Window。
  4. 在View添加到Window上后执行Activity的makeVisible方法显示View视图。
最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 211,123评论 6 490
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 90,031评论 2 384
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 156,723评论 0 345
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 56,357评论 1 283
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 65,412评论 5 384
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 49,760评论 1 289
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 38,904评论 3 405
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 37,672评论 0 266
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 44,118评论 1 303
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 36,456评论 2 325
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 38,599评论 1 340
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 34,264评论 4 328
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 39,857评论 3 312
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 30,731评论 0 21
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,956评论 1 264
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 46,286评论 2 360
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 43,465评论 2 348

推荐阅读更多精彩内容