Android:Activity赴魁,Window,DecorView之間的關(guān)系

一钝诚、前言

今天我們來講講Activity颖御,Window,DecorView之間的關(guān)系凝颇。

二潘拱、Activity

我們布局一個頁面一般都是從setContentView開始,傳入我們的XML布局拧略,那么我們就從這個方法入手芦岂。
我們要知道每個Activity都是有一個自己的Window負(fù)責(zé)來顯示頁面的,而Window是一個抽象類垫蛆,他的唯一實現(xiàn)類是PhoneWindow(對Window不太了解的以后我在找時間講解一下)禽最。我們Activity調(diào)用setContentView()方法其實調(diào)用的就是PhoneWindow的setContentView()方法,下面我們來看一下Activity中setContentView()源碼:

    public void setContentView(@LayoutRes int layoutResID) {
        getWindow().setContentView(layoutResID);
        initWindowDecorActionBar();
    }

可以看到這里調(diào)用的是getWindow().setContentView()袱饭,這個getWindow()是什么:

    public Window getWindow() {
        return mWindow;
    }

這個mWindow哪里創(chuàng)建的川无,就在Activity的attach()方法中:

    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, ActivityConfigCallback activityConfigCallback) {
        attachBaseContext(context);

        mFragments.attachHost(null /*parent*/);

        mWindow = new PhoneWindow(this, window, activityConfigCallback);
        ......
    }

下面我們來看看PhoneWindow的setContentView()方法:

    @Override
    public void setContentView(int layoutResID) {
        // 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)) {
            final Scene newScene = Scene.getSceneForLayout(mContentParent, layoutResID,
                    getContext());
            transitionTo(newScene);
        } else {
            mLayoutInflater.inflate(layoutResID, mContentParent);
        }
        mContentParent.requestApplyInsets();
        final Callback cb = getCallback();
        if (cb != null && !isDestroyed()) {
            cb.onContentChanged();
        }
        mContentParentExplicitlySet = true;
    }

這里先判斷mContentParent是否為空,為空則執(zhí)行installDecor()虑乖,這個跟初始化DecorView有關(guān)懦趋。然后判斷是否需要轉(zhuǎn)場動畫,不需要就直接加載layout到mContentParent上疹味。那么我們先來看一下DecorView仅叫,ContentParent都是什么:

    // This is the top-level view of the window, containing the window decor.
    private DecorView mDecor;

    // This is the view in which the window contents are placed. It is either
    // mDecor itself, or a child of mDecor where the contents go.
    ViewGroup mContentParent;

這里根據(jù)官方注釋,DecorView是Window上的頂級視圖佛猛,mContentParent是放置窗口內(nèi)容的視圖,要么是
mDecor本身坠狡,或內(nèi)容所在的mDecor的子級继找。我們再跟下去看一下DecorView的源碼,看看他是什么View:

public class DecorView extends FrameLayout implements RootViewSurfaceTaker, WindowCallbacks {
    private static final String TAG = "DecorView";
    ......
}

可以看到DecorView繼承自FrameLayout 逃沿。好了婴渡,那我們繼續(xù)來看installDecor()都做了什么:

    private void installDecor() {
        mForceDecorInstall = false;
        if (mDecor == null) {
            mDecor = generateDecor(-1);
            .......
        } else {
            mDecor.setWindow(this);
        }
        if (mContentParent == null) {
            mContentParent = generateLayout(mDecor);
            ......
        }else
        ......
    }

這里我們可以看到 先創(chuàng)建DectorView,然后再創(chuàng)建了mContentParent 凯亮,我們先來看generateDecor()都做了什么:

    protected DecorView generateDecor(int featureId) {
        // System process doesn't have application context and in that case we need to directly use
        // the context we have. Otherwise we want the application context, so we don't cling to the
        // activity.
        Context context;
        if (mUseDecorContext) {
            Context applicationContext = getContext().getApplicationContext();
            if (applicationContext == null) {
                context = getContext();
            } else {
                context = new DecorContext(applicationContext, getContext());
                if (mTheme != -1) {
                    context.setTheme(mTheme);
                }
            }
        } else {
            context = getContext();
        }
        return new DecorView(context, featureId, this, getAttributes());
    }

這里直接實例化了一個DectorView边臼,我們再來看看generateLayout():

    protected ViewGroup generateLayout(DecorView decor) {
        // Apply data from current theme.

        TypedArray a = getWindowStyle();
        ......
        mIsFloating = a.getBoolean(R.styleable.Window_windowIsFloating, false);
        int flagsToUpdate = (FLAG_LAYOUT_IN_SCREEN|FLAG_LAYOUT_INSET_DECOR)
                & (~getForcedWindowFlags());
        if (mIsFloating) {
            setLayout(WRAP_CONTENT, WRAP_CONTENT);
            setFlags(0, flagsToUpdate);
        } else {
            setFlags(FLAG_LAYOUT_IN_SCREEN|FLAG_LAYOUT_INSET_DECOR, flagsToUpdate);
        }
        ......
        // Inflate the window decor.

        int layoutResource;
        int features = getLocalFeatures();
        ......
        } else {
            // Embedded, so no decoration is needed.
            layoutResource = R.layout.screen_simple;
            // System.out.println("Simple!");
        }
        mDecor.startChanging();
        mDecor.onResourcesLoaded(mLayoutInflater, layoutResource);

        ViewGroup contentParent = (ViewGroup)findViewById(ID_ANDROID_CONTENT);
        ......
        return contentParent;

這里邏輯代碼非常多,我就貼出一些關(guān)鍵的地方假消。這里先是通過getWindowStyle()獲取當(dāng)前Window的TypedArray柠并,然后再通過TypedArray對Feature狀態(tài)位進行設(shè)置,比如判斷當(dāng)前Window是否為懸浮狀態(tài),是否全屏臼予,是否顯示ActionBar鸣戴,是否透明等等,最后就是通過設(shè)置好的Feature獲取對應(yīng)的layoutResource粘拾,這些layoutResource都是Android系統(tǒng)內(nèi)提供的窄锅,這些在依賴庫里都是可以找到的,這里我們以screen_simple舉例:

1602601388(1).png
1602601440(1).png

下面我們來看一下這個xml布局:

<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:fitsSystemWindows="true"
    android:orientation="vertical">
    <ViewStub android:id="@+id/action_mode_bar_stub"
              android:inflatedId="@+id/action_mode_bar"
              android:layout="@layout/action_mode_bar"
              android:layout_width="match_parent"
              android:layout_height="wrap_content"
              android:theme="?attr/actionBarTheme" />
    <FrameLayout
         android:id="@android:id/content"
         android:layout_width="match_parent"
         android:layout_height="match_parent"
         android:foregroundInsidePadding="false"
         android:foregroundGravity="fill_horizontal|top"
         android:foreground="?android:attr/windowContentOverlay" />
</LinearLayout>

可以看到根節(jié)點是LinearLayout缰雇,當(dāng)然加載不同的xml布局的根節(jié)點也不同入偷,只是這里是LinearLayout。ViewStub是用來延遲加載的一種組件械哟,是用來動態(tài)顯示actionBar的疏之,而id為content的這個FrameLayout就是我們真正加載布局的地方了,我們傳入的布局就放在這里戒良。記住這個android:id="@android:id/content"体捏,其他類型的布局樣式不同,根節(jié)點不同糯崎,但是真正加載用戶布局的id始終都為content几缭。
然后我們看一下mDecor.onResourcesLoaded(mLayoutInflater, layoutResource)方法:

    void onResourcesLoaded(LayoutInflater inflater, int layoutResource) {
        if (mBackdropFrameRenderer != null) {
            loadBackgroundDrawablesIfNeeded();
            mBackdropFrameRenderer.onResourcesLoaded(
                    this, mResizingBackgroundDrawable, mCaptionBackgroundDrawable,
                    mUserCaptionBackgroundDrawable, getCurrentColor(mStatusColorViewState),
                    getCurrentColor(mNavigationColorViewState));
        }

        mDecorCaptionView = createDecorCaptionView(inflater);
        final View root = inflater.inflate(layoutResource, null);
        if (mDecorCaptionView != null) {
            if (mDecorCaptionView.getParent() == null) {
                addView(mDecorCaptionView,
                        new ViewGroup.LayoutParams(MATCH_PARENT, MATCH_PARENT));
            }
            mDecorCaptionView.addView(root,
                    new ViewGroup.MarginLayoutParams(MATCH_PARENT, MATCH_PARENT));
        } else {

            // Put it below the color views.
            addView(root, 0, new ViewGroup.LayoutParams(MATCH_PARENT, MATCH_PARENT));
        }
        mContentRoot = (ViewGroup) root;
        initializeElevation();
    }

這里是將獲取的layoutResource渲染出來,然后加入到DectorView中沃呢。
最后就是ViewGroup contentParent = (ViewGroup)findViewById(ID_ANDROID_CONTENT)獲取到contentParent 返回賦值給mContentParent年栓。我們來看一下ID_ANDROID_CONTENT是什么:

    /**
     * The ID that the main layout in the XML layout file should have.
     */
    public static final int ID_ANDROID_CONTENT = com.android.internal.R.id.content;

到這里我們的mContentParent容器就創(chuàng)建完成了,我們現(xiàn)在來看一下Activity薄霜,Window(PhoneWindow)某抓,DectorView,ContentParent的大概關(guān)系圖:

1602603473(1).png

三惰瓜、AppCompatActivity

這個AppComPatActivity是我們目前常用的繼承對象否副,為什么要拿出來說呢,因為他跟Activity中UI繪制流程不一樣啊崎坊,這是谷歌后期推出為了給Activity各種問題填坑用的备禀,我們來看看他的setContentView():

    @Override
    public void setContentView(@LayoutRes int layoutResID) {
        getDelegate().setContentView(layoutResID);
    }

漂亮,跟Activity中完全不一樣奈揍,感謝谷歌曲尸。我們來看看getDelegate()是什么:

    /**
     * @return The {@link AppCompatDelegate} being used by this Activity.
     */
    @NonNull
    public AppCompatDelegate getDelegate() {
        if (mDelegate == null) {
            mDelegate = AppCompatDelegate.create(this, this);
        }
        return mDelegate;
    }

再來看看AppCompatDelegate.create(this, this):

    /**
     * Create an {@link androidx.appcompat.app.AppCompatDelegate} to use with {@code activity}.
     *
     * @param callback An optional callback for AppCompat specific events
     */
    @NonNull
    public static AppCompatDelegate create(@NonNull Activity activity,
            @Nullable AppCompatCallback callback) {
        return new AppCompatDelegateImpl(activity, callback);
    }

繼續(xù),AppCompatDelegateImpl(activity, callback):

    AppCompatDelegateImpl(Activity activity, AppCompatCallback callback) {
        this(activity, null, callback, activity);
    }

    ......

    private AppCompatDelegateImpl(Context context, Window window, AppCompatCallback callback,
            Object host) {
        mContext = context;
        mAppCompatCallback = callback;
        mHost = host;

        if (mLocalNightMode == MODE_NIGHT_UNSPECIFIED && mHost instanceof Dialog) {
            final AppCompatActivity activity = tryUnwrapContext();
            if (activity != null) {
                // This code path is used to detect when this Delegate is a child Delegate from
                // an Activity, primarily for Dialogs. Dialogs use the Activity as it's Context,
                // so we want to make sure that the this 'child' delegate does not interfere
                // with the Activity config. The simplest way to do that is to match the
                // outer Activity's local night mode
                mLocalNightMode = activity.getDelegate().getLocalNightMode();
            }
        }
        if (mLocalNightMode == MODE_NIGHT_UNSPECIFIED) {
            // Try and read the current night mode from our static store
            final Integer value = sLocalNightModes.get(mHost.getClass());
            if (value != null) {
                mLocalNightMode = value;
                // Finally remove the value
                sLocalNightModes.remove(mHost.getClass());
            }
        }

        if (window != null) {
            attachToWindow(window);
        }

        // Preload appcompat-specific handling of drawables that should be handled in a special
        // way (for tinting etc). After the following line completes, calls from AppCompatResources
        // to ResourceManagerInternal (in appcompat-resources) will handle those internal drawable
        // paths correctly without having to go through AppCompatDrawableManager APIs.
        AppCompatDrawableManager.preload();
    }

看到這里我們發(fā)現(xiàn)getDelegate()是獲取代理的方法男翰,會通過mDelegate = AppCompatDelegate.create(this, this)來創(chuàng)建代理對象另患,最后會的的是AppCompatDelegateImpl對象。這里要說明一點蛾绎,我這個是AndroidX的代碼昆箕,如果你的項目沒有轉(zhuǎn)到AndroidX是有些不一樣的鸦列,但是大方向沒問題。我們來看一下AppCompatDelegateImpl中setContentView() 方法:

    @Override
    public void setContentView(int resId) {
        ensureSubDecor();
        ViewGroup contentParent = mSubDecor.findViewById(android.R.id.content);
        contentParent.removeAllViews();
        LayoutInflater.from(mContext).inflate(resId, contentParent);
        mAppCompatWindowCallback.getWrapped().onContentChanged();
    }

和Activity中差不多为严,也是獲取到了contentParent后去渲染用戶布局敛熬,但是這里是ensureSubDecor(),我們來看一下這個方法:

    private void ensureSubDecor() {
        if (!mSubDecorInstalled) {
            mSubDecor = createSubDecor();
        ......
    }

繼續(xù)看createSubDecor():

    private ViewGroup createSubDecor() {
        TypedArray a = mContext.obtainStyledAttributes(R.styleable.AppCompatTheme);

        if (!a.hasValue(R.styleable.AppCompatTheme_windowActionBar)) {
            a.recycle();
            throw new IllegalStateException(
                    "You need to use a Theme.AppCompat theme (or descendant) with this activity.");
        }
        ......
        // Now let's make sure that the Window has installed its decor by retrieving it
        ensureWindow();
        mWindow.getDecorView();

        final LayoutInflater inflater = LayoutInflater.from(mContext);
        ViewGroup subDecor = null;
        if (!mWindowNoTitle) {
            if (mIsFloating) {
                // If we're floating, inflate the dialog title decor
                subDecor = (ViewGroup) inflater.inflate(
                        R.layout.abc_dialog_title_material, null);
            ......
        } else {
            if (mOverlayActionMode) {
                subDecor = (ViewGroup) inflater.inflate(
                        R.layout.abc_screen_simple_overlay_action_mode, null);
            } else {
                subDecor = (ViewGroup) inflater.inflate(R.layout.abc_screen_simple, null);
            }
            ......
        }
        ......
        final ContentFrameLayout contentView = (ContentFrameLayout) subDecor.findViewById(
                R.id.action_bar_activity_content);

        final ViewGroup windowContentView = (ViewGroup) mWindow.findViewById(android.R.id.content);
        if (windowContentView != null) {
            // There might be Views already added to the Window's content view so we need to
            // migrate them to our content view
            while (windowContentView.getChildCount() > 0) {
                final View child = windowContentView.getChildAt(0);
                windowContentView.removeViewAt(0);
                contentView.addView(child);
            }

            // Change our content FrameLayout to use the android.R.id.content id.
            // Useful for fragments.
            windowContentView.setId(View.NO_ID);
            contentView.setId(android.R.id.content);

            // The decorContent may have a foreground drawable set (windowContentOverlay).
            // Remove this as we handle it ourselves
            if (windowContentView instanceof FrameLayout) {
                ((FrameLayout) windowContentView).setForeground(null);
            }
        }

        // Now set the Window's content view with the decor
        mWindow.setContentView(subDecor);
        ......

        return subDecor;
    }

這個方法邏輯也是非常多第股,我就截取一些重要的展示一下应民。可以看到剛開始跟Activity是一樣的夕吻,獲取TypedArray從而來判斷加載那個xml布局诲锹,但是中間多了一個mWindow.getDecorView()方法,我們來看看這個方法涉馅,在PhoneWindow中:

    @Override
    public final View getDecorView() {
        if (mDecor == null || mForceDecorInstall) {
            installDecor();
        }
        return mDecor;
    }

熟悉的味道归园,不講了,跟之前一樣獲取到DectorView稚矿,下面繼續(xù)看庸诱,我們看到一個熟悉的布局abc_screen_simple,之前的是screen_simple晤揣,我們來找一下這個布局:

1602605344(1).png
1602605372(1).png

老規(guī)矩桥爽,看一下這個布局xml:

<androidx.appcompat.widget.FitWindowsLinearLayout
    xmlns:android="http://schemas.android.com/apk/res/android"
    android:id="@+id/action_bar_root"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:orientation="vertical"
    android:fitsSystemWindows="true">

    <androidx.appcompat.widget.ViewStubCompat
        android:id="@+id/action_mode_bar_stub"
        android:inflatedId="@+id/action_mode_bar"
        android:layout="@layout/abc_action_mode_bar"
        android:layout_width="match_parent"
        android:layout_height="wrap_content" />

    <include layout="@layout/abc_screen_content_include" />

</androidx.appcompat.widget.FitWindowsLinearLayout>

繼續(xù)看abc_screen_content_include:

<merge xmlns:android="http://schemas.android.com/apk/res/android">

    <androidx.appcompat.widget.ContentFrameLayout
            android:id="@id/action_bar_activity_content"
            android:layout_width="match_parent"
            android:layout_height="match_parent"
            android:foregroundGravity="fill_horizontal|top"
            android:foreground="?android:attr/windowContentOverlay" />

</merge>

嗯?怎么回事昧识,這里id怎么是action_bar_activity_content钠四,之前是content啊,我們繼續(xù)看createSubDecor()后續(xù)代碼:

        final ContentFrameLayout contentView = (ContentFrameLayout) subDecor.findViewById(
                R.id.action_bar_activity_content);

        final ViewGroup windowContentView = (ViewGroup) mWindow.findViewById(android.R.id.content);
        if (windowContentView != null) {
            // There might be Views already added to the Window's content view so we need to
            // migrate them to our content view
            while (windowContentView.getChildCount() > 0) {
                final View child = windowContentView.getChildAt(0);
                windowContentView.removeViewAt(0);
                contentView.addView(child);
            }

            // Change our content FrameLayout to use the android.R.id.content id.
            // Useful for fragments.
            windowContentView.setId(View.NO_ID);
            contentView.setId(android.R.id.content);

            // The decorContent may have a foreground drawable set (windowContentOverlay).
            // Remove this as we handle it ourselves
            if (windowContentView instanceof FrameLayout) {
                ((FrameLayout) windowContentView).setForeground(null);
            }
        }

        // Now set the Window's content view with the decor
        mWindow.setContentView(subDecor);
        ......

        return subDecor;

contentView就是subDecor中id為action_bar_activity_content的ContentFrameLayout跪楞,windowContentView就是PhoneWindow中DectorView中id為content的FrameLayout缀去,然后windowContentView的id設(shè)置為了NO_ID,contentView的id設(shè)置為了android.R.id.content甸祭,再調(diào)用mWindow.setContentView把subDecor設(shè)置進去了缕碎,我們來看看PhoneWindow.setContentView(View) 方法:

    @Override
    public void setContentView(View view) {
        setContentView(view, new ViewGroup.LayoutParams(MATCH_PARENT, MATCH_PARENT));
    }

繼續(xù):

    @Override
    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;
    }

這邊mContentParent 是不會為空的,因為之前mWindow.getDecorView()這個方法已經(jīng)創(chuàng)建了DectorView池户,以及mContentParent咏雌,所以在沒有轉(zhuǎn)場動畫的情況下,直接調(diào)用mContentParent.addView(view, params)將subDecor放入了mContentParent中煞檩。

最后我們來看看AppCompatActivity結(jié)構(gòu)圖:

1602606366(1).png
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末处嫌,一起剝皮案震驚了整個濱河市栅贴,隨后出現(xiàn)的幾起案子斟湃,更是在濱河造成了極大的恐慌,老刑警劉巖檐薯,帶你破解...
    沈念sama閱讀 217,542評論 6 504
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件凝赛,死亡現(xiàn)場離奇詭異注暗,居然都是意外死亡,警方通過查閱死者的電腦和手機墓猎,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 92,822評論 3 394
  • 文/潘曉璐 我一進店門捆昏,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人毙沾,你說我怎么就攤上這事骗卜。” “怎么了左胞?”我有些...
    開封第一講書人閱讀 163,912評論 0 354
  • 文/不壞的土叔 我叫張陵寇仓,是天一觀的道長。 經(jīng)常有香客問我烤宙,道長遍烦,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 58,449評論 1 293
  • 正文 為了忘掉前任躺枕,我火速辦了婚禮服猪,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘拐云。我一直安慰自己罢猪,他們只是感情好,可當(dāng)我...
    茶點故事閱讀 67,500評論 6 392
  • 文/花漫 我一把揭開白布慨丐。 她就那樣靜靜地躺著坡脐,像睡著了一般。 火紅的嫁衣襯著肌膚如雪房揭。 梳的紋絲不亂的頭發(fā)上备闲,一...
    開封第一講書人閱讀 51,370評論 1 302
  • 那天,我揣著相機與錄音捅暴,去河邊找鬼恬砂。 笑死,一個胖子當(dāng)著我的面吹牛蓬痒,可吹牛的內(nèi)容都是我干的泻骤。 我是一名探鬼主播,決...
    沈念sama閱讀 40,193評論 3 418
  • 文/蒼蘭香墨 我猛地睜開眼梧奢,長吁一口氣:“原來是場噩夢啊……” “哼狱掂!你這毒婦竟也來了?” 一聲冷哼從身側(cè)響起亲轨,我...
    開封第一講書人閱讀 39,074評論 0 276
  • 序言:老撾萬榮一對情侶失蹤趋惨,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后惦蚊,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體器虾,經(jīng)...
    沈念sama閱讀 45,505評論 1 314
  • 正文 獨居荒郊野嶺守林人離奇死亡讯嫂,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 37,722評論 3 335
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發(fā)現(xiàn)自己被綠了兆沙。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片欧芽。...
    茶點故事閱讀 39,841評論 1 348
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖葛圃,靈堂內(nèi)的尸體忽然破棺而出千扔,到底是詐尸還是另有隱情,我是刑警寧澤库正,帶...
    沈念sama閱讀 35,569評論 5 345
  • 正文 年R本政府宣布昏鹃,位于F島的核電站,受9級特大地震影響诀诊,放射性物質(zhì)發(fā)生泄漏洞渤。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點故事閱讀 41,168評論 3 328
  • 文/蒙蒙 一属瓣、第九天 我趴在偏房一處隱蔽的房頂上張望载迄。 院中可真熱鬧,春花似錦抡蛙、人聲如沸护昧。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,783評論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽惋耙。三九已至,卻和暖如春熊昌,著一層夾襖步出監(jiān)牢的瞬間绽榛,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 32,918評論 1 269
  • 我被黑心中介騙來泰國打工婿屹, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留灭美,地道東北人。 一個月前我還...
    沈念sama閱讀 47,962評論 2 370
  • 正文 我出身青樓昂利,卻偏偏與公主長得像届腐,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子蜂奸,可洞房花燭夜當(dāng)晚...
    茶點故事閱讀 44,781評論 2 354