setContentView()、布局的顯示繪制流程荔茬、LayoutInflater分析

1霸琴、setContentView()

1.1 Activity中雄家,setContentView(),最終是調(diào)用到的是PhoneWindow的setContentView()去設(shè)置布局氮双。
1.2 PhoneWindow的setContentView()就是去創(chuàng)建一個DecorView加載系統(tǒng)默認的布局R.layout.screen_simple里面有個id為R.id.content(mContentParent)碰酝,然后解析我們設(shè)置的資源布局,然后添加到mContentParent戴差。
1.3 PhoneWindow的setContentView() 只是去創(chuàng)建送爸、解析我們的布局,執(zhí)行完以后就什么都沒干了暖释,那么布局是怎么顯示出來的袭厂。

setContentView()圖解

2、布局的顯示繪制流程

在setContentView(layoutResID)中有一行代碼球匕,inflate(resId,mContentParent);會來到 ViewRootImpl 類的 requestLayout() 方法纹磺。這就是View繪制流程的入口。
mLayoutInflater.inflate(layoutResID, mContentParent);

    @Override
    public void requestLayout() {
        if (!mHandlingLayoutInLayoutRequest) {
            checkThread();
            mLayoutRequested = true;
            scheduleTraversals();
        }
    }
    void scheduleTraversals() {
        if (!mTraversalScheduled) {
            mTraversalScheduled = true;
            mTraversalBarrier = mHandler.getLooper().getQueue().postSyncBarrier();
            //執(zhí)行TraversalRunnable的run()方法
            mChoreographer.postCallback(
                    Choreographer.CALLBACK_TRAVERSAL, mTraversalRunnable, null);
            if (!mUnbufferedInputDispatch) {
                scheduleConsumeBatchedInput();
            }
            notifyRendererOfFramePending();
            pokeDrawLockIfNeeded();
        }
    }
    void doTraversal() {
        if (mTraversalScheduled) {
            mTraversalScheduled = false;
            mHandler.getLooper().getQueue().removeSyncBarrier(mTraversalBarrier);
            if (mProfile) {
                Debug.startMethodTracing("ViewAncestor");
            }
            // 一系列方法下來之后亮曹,進入performTraversals() 這個方法很長..
            performTraversals();
            if (mProfile) {
                Debug.stopMethodTracing();
                mProfile = false;
            }
        }
    }

這里只貼一些關(guān)鍵代碼- -

    private void performTraversals() {
        // ... ...
        // Ask host how big it wants to be
        performMeasure(childWidthMeasureSpec, childHeightMeasureSpec);
        // ... ...
        performLayout(lp, mWidth, mHeight);
        // ... ...
        performDraw();
    }

接下來進入繪制的關(guān)鍵三部

2.1performMeasure()
    private void performMeasure(int childWidthMeasureSpec, int childHeightMeasureSpec) {
        if (mView == null) {
            return;
        }
        Trace.traceBegin(Trace.TRACE_TAG_VIEW, "measure");
        try {
            mView.measure(childWidthMeasureSpec, childHeightMeasureSpec);
        } finally {
            Trace.traceEnd(Trace.TRACE_TAG_VIEW);
        }
    }

    public final void measure(int widthMeasureSpec, int heightMeasureSpec) {
                //只貼關(guān)鍵代碼....
                // measure ourselves, this should set the measured dimension flag back
                onMeasure(widthMeasureSpec, heightMeasureSpec);
    }

以LinearLayout布局為例

   @Override
    protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
        if (mOrientation == VERTICAL) {
            // 我們以垂直為例
            measureVertical(widthMeasureSpec, heightMeasureSpec);
        } else {
            measureHorizontal(widthMeasureSpec, heightMeasureSpec);
        }
    }
   void measureVertical(int widthMeasureSpec, int heightMeasureSpec) {
        // See how tall everyone is. Also remember max width.
        for (int i = 0; i < count; ++i) {
            final View child = getVirtualChildAt(i);
            // 測量子孩子
            measureChildBeforeLayout(child, i, widthMeasureSpec, 0,
                        heightMeasureSpec, usedHeight);

            final int childHeight = child.getMeasuredHeight();

            final int totalLength = mTotalLength;
            // 高度是子View的高度不斷的疊加
            mTotalLength = Math.max(totalLength, totalLength + childHeight + lp.topMargin +
                       lp.bottomMargin + getNextLocationOffset(child));
        }
        int heightSize = mTotalLength;
        // Check against our minimum height
        heightSize = Math.max(heightSize, getSuggestedMinimumHeight());
        
        // Reconcile our calculated size with the heightMeasureSpec
        int heightSizeAndState = resolveSizeAndState(heightSize, heightMeasureSpec, 0);
        // 設(shè)置寬高
        setMeasuredDimension(resolveSizeAndState(maxWidth, widthMeasureSpec, childState),
                heightSizeAndState);
    }

LinearLayout在調(diào)用onMeasure()方法的時候橄杨,會不斷的循環(huán)測量子View,如果是垂直方向照卦,高度是子View的高度疊加式矫,我們現(xiàn)在來看看是怎么測量子View的。

    void measureChildBeforeLayout(View child, int childIndex,
            int widthMeasureSpec, int totalWidth, int heightMeasureSpec,
            int totalHeight) {
        measureChildWithMargins(child, widthMeasureSpec, totalWidth,
                heightMeasureSpec, totalHeight);
    }
    public static int getChildMeasureSpec(int spec, int padding, int childDimension) {
        //獲取當前Parent View的Mode和Size
        int specMode = MeasureSpec.getMode(spec);
        int specSize = MeasureSpec.getSize(spec);
        //獲取Parent size與padding差值(也就是Parent剩余大姓痢)衷佃,若差值小于0直接返回0
        int size = Math.max(0, specSize - padding);
        //定義返回值存儲變量
        int resultSize = 0;
        int resultMode = 0;
        //依據(jù)當前Parent的Mode進行switch分支邏輯
        switch (specMode) {
        // Parent has imposed an exact size on us
        case MeasureSpec.EXACTLY:
            if (childDimension >= 0) {
                //如果child的layout_w和h屬性在xml或者java中給予具體大于等于0的數(shù)值
                //設(shè)置child的size為真實layout_w和h屬性值,mode為EXACTLY
                resultSize = childDimension;
                resultMode = MeasureSpec.EXACTLY;
            } else if (childDimension == LayoutParams.MATCH_PARENT) {
                // Child wants to be our size. So be it.
                //如果child的layout_wOrh屬性在xml或者java中給予MATCH_PARENT
                //設(shè)置child的size為size蹄葱,mode為EXACTLY
                resultSize = size;
                resultMode = MeasureSpec.EXACTLY;
            } else if (childDimension == LayoutParams.WRAP_CONTENT) {
                // Child wants to determine its own size. It can't be
                // bigger than us.
                //如果child的layout_wOrh屬性在xml或者java中給予WRAP_CONTENT
                //設(shè)置child的size為size氏义,mode為AT_MOST
                resultSize = size;
                resultMode = MeasureSpec.AT_MOST;
            }
            break;

        // Parent has imposed a maximum size on us
        case MeasureSpec.AT_MOST:
            if (childDimension >= 0) {
                // ......
            } else if (childDimension == LayoutParams.MATCH_PARENT) {
                // Child wants to be our size, but our size is not fixed.
                // Constrain child to not be bigger than us.
                // 如果父 View 是 AT_MOST 就算子 View 是 MATCH_PARENT,
                // 其實子View獲得的測量模式還是AT_MOST
                resultSize = size;
                resultMode = MeasureSpec.AT_MOST;
            } else if (childDimension == LayoutParams.WRAP_CONTENT) {
                // Child wants to determine its own size. It can't be
                // bigger than us.
                resultSize = size;
                resultMode = MeasureSpec.AT_MOST;
            }
            break;

            // ......
        }
        //noinspection ResourceType
        return MeasureSpec.makeMeasureSpec(resultSize, resultMode);
    }

這里再貼下图云,getChildMeasureSpec()的值介紹:

image.png

總結(jié):View的繪制流程第一步是onMeasure()惯悠,該方法用來測量和指定布局到底占多大的寬高,因為控件的寬高是由父布局和本身來決定的竣况,所以測量是不斷的往內(nèi)走克婶,而最終確定寬高是由內(nèi)不斷的往外走,是遞歸的方式丹泉。

2.2performLayout()
private void performLayout(WindowManager.LayoutParams lp, int desiredWindowWidth,
            int desiredWindowHeight) {
        final View host = mView;
        // 調(diào)用layout()方法
        host.layout(0, 0, host.getMeasuredWidth(), host.getMeasuredHeight());
    }

    public void layout(int l, int t, int r, int b) {
        onLayout(changed, l, t, r, b);
    }

同樣以LinearLayout為例

    @Override
    protected void onLayout(boolean changed, int l, int t, int r, int b) {
        if (mOrientation == VERTICAL) {
            // 我們以垂直為例
            layoutVertical(l, t, r, b);
        } else {
            layoutHorizontal(l, t, r, b);
        }
    }
    void layoutVertical(int left, int top, int right, int bottom) {
        final int paddingLeft = mPaddingLeft;

        int childTop;
        int childLeft;

        // Where right end of child should go
        //計算父窗口推薦的子View寬度
        final int width = right - left;
        //計算父窗口推薦的子View右側(cè)位置
        int childRight = width - mPaddingRight;

        // Space available for child
        //child可使用空間大小
        int childSpace = width - paddingLeft - mPaddingRight;
        //通過ViewGroup的getChildCount方法獲取ViewGroup的子View個數(shù)
        final int count = getVirtualChildCount();
        //獲取Gravity屬性設(shè)置
        final int majorGravity = mGravity & Gravity.VERTICAL_GRAVITY_MASK;
        final int minorGravity = mGravity & Gravity.RELATIVE_HORIZONTAL_GRAVITY_MASK;
        //依據(jù)majorGravity計算childTop的位置值
        switch (majorGravity) {
           case Gravity.BOTTOM:
               // mTotalLength contains the padding already
               childTop = mPaddingTop + bottom - top - mTotalLength;
               break;

               // mTotalLength contains the padding already
           case Gravity.CENTER_VERTICAL:
               childTop = mPaddingTop + (bottom - top - mTotalLength) / 2;
               break;

           case Gravity.TOP:
           default:
               childTop = mPaddingTop;
               break;
        }
        //重點G橛!摹恨!開始遍歷
        for (int i = 0; i < count; i++) {
            final View child = getVirtualChildAt(i);
            if (child == null) {
                childTop += measureNullChild(i);
            } else if (child.getVisibility() != GONE) {
                //LinearLayout中其子視圖顯示的寬和高由measure過程來決定的筋岛,因此measure過程的意義就是為layout過程提供視圖顯示范圍的參考值
                final int childWidth = child.getMeasuredWidth();
                final int childHeight = child.getMeasuredHeight();
                //獲取子View的LayoutParams
                final LinearLayout.LayoutParams lp =
                        (LinearLayout.LayoutParams) child.getLayoutParams();

                int gravity = lp.gravity;
                if (gravity < 0) {
                    gravity = minorGravity;
                }
                final int layoutDirection = getLayoutDirection();
                final int absoluteGravity = Gravity.getAbsoluteGravity(gravity, layoutDirection);
                //依據(jù)不同的absoluteGravity計算childLeft位置
                switch (absoluteGravity & Gravity.HORIZONTAL_GRAVITY_MASK) {
                    case Gravity.CENTER_HORIZONTAL:
                        childLeft = paddingLeft + ((childSpace - childWidth) / 2)
                                + lp.leftMargin - lp.rightMargin;
                        break;

                    case Gravity.RIGHT:
                        childLeft = childRight - childWidth - lp.rightMargin;
                        break;

                    case Gravity.LEFT:
                    default:
                        childLeft = paddingLeft + lp.leftMargin;
                        break;
                }

                if (hasDividerBeforeChildAt(i)) {
                    childTop += mDividerHeight;
                }

                childTop += lp.topMargin;
                //通過垂直排列計算調(diào)運child的layout設(shè)置child的位置
                setChildFrame(child, childLeft, childTop + getLocationOffset(child),
                        childWidth, childHeight);
                childTop += childHeight + lp.bottomMargin + getNextLocationOffset(child);

                i += getChildrenSkipCount(child, i);
            }
        }
    }

總結(jié):從上面分析可以看出layout也是從頂層父View向子View的遞歸調(diào)用view.layout方法的過程,即父View根據(jù)第一步performMeasure晒哄,來獲取子View所的布局大小和布局參數(shù)睁宰,將子View放在合適的位置上肪获。

2.3performDraw()
   private void performDraw() {
        try {
            draw(fullRedrawNeeded);
        } finally {
            mIsDrawing = false;
            Trace.traceEnd(Trace.TRACE_TAG_VIEW);
        }
    }

    private void draw(boolean fullRedrawNeeded) {
        Surface surface = mSurface;
        if (!surface.isValid()) {
            return;
        }
        
        if (!drawSoftware(surface, mAttachInfo, xOffset, yOffset, scalingRequired, dirty)) {
            return;
        }
    }

    private boolean drawSoftware(Surface surface, AttachInfo attachInfo, int xoff, int yoff,
            boolean scalingRequired, Rect dirty) {
        // Draw with software renderer.
        final Canvas canvas;
        final int left = dirty.left;
        final int top = dirty.top;
        final int right = dirty.right;
        final int bottom = dirty.bottom;
        canvas = mSurface.lockCanvas(dirty);
        // ... ...
        mView.draw(canvas);
    }

3、LayoutInflater分析

3.1如何獲取LayoutInflater柒傻?

通過context獲取系統(tǒng)的服務(wù),context.getSystemService()是一個抽象方法孝赫,找到其實現(xiàn)類contextImpl.getSystemService(),通過系統(tǒng)服務(wù)注冊表獲群旆青柄;
注冊表中的集合SYSTEM_SERVICE_FETCHERS,是在靜態(tài)代碼塊中就初始化了违孝。

//LayoutInflater 類
    public static LayoutInflater from(Context context) {
        LayoutInflater LayoutInflater =
                (LayoutInflater) context.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
        if (LayoutInflater == null) {
            throw new AssertionError("LayoutInflater not found.");
        }
        return LayoutInflater;
    }

//ContextImpl實現(xiàn)類的getSystemService()方法刹前,通過系統(tǒng)服務(wù)注冊表獲取
    public Object getSystemService(String name) {
        return SystemServiceRegistry.getSystemService(this, name);
    }

//SystemServiceRegistry類
    public static Object getSystemService(ContextImpl ctx, String name) {
        ServiceFetcher<?> fetcher = SYSTEM_SERVICE_FETCHERS.get(name);
        return fetcher != null ? fetcher.getService(ctx) : null;
    }

// 靜態(tài)的代碼塊中
    static{
         // 注冊LayoutInflater服務(wù)
         registerService(Context.LAYOUT_INFLATER_SERVICE, LayoutInflater.class,
                new CachedServiceFetcher<LayoutInflater>() {
            @Override
            public LayoutInflater createService(ContextImpl ctx) {
                return new PhoneLayoutInflater(ctx.getOuterContext());
            }});
         }
         // 注冊很多的其他服務(wù)......
    }

大致總結(jié)下獲取LayoutInflater思路,通過Context的實現(xiàn)類ContextImpl獲取的雌桑,最終是通過SystemServiceRegistry.getSystemService()方法喇喉,而SYSTEM_SERVICE_FETCHERS是一個靜態(tài)的HashMap,初始化是在靜態(tài)代碼塊中通過registerService注冊了很多服務(wù)校坑。
LayoutInflater其實是一個系統(tǒng)的服務(wù)拣技,每次獲取到的都是同一個靜態(tài)單例。

3.2如何使用LayoutInflater耍目?
3.3布局的View是如何被實例化的膏斤?

我們先看下加載布局的三種方式:

1.View.inflate(context,layoutId,parent);
2.LayoutInflater.from(context).inflate(layoutId,parent);
3.LayoutInflater.from(context).inflate(layoutId,parent,attachToRoot);

View.inflate()方法,最終也是調(diào)用到了LayoutInflater.from(context).inflate(layoutId,parent);

   public static View inflate(Context context, @LayoutRes int resource, ViewGroup root) {
        LayoutInflater factory = LayoutInflater.from(context);
        return factory.inflate(resource, root);
    }

LayoutInflater.from(context).inflate(layoutId,parent);最終也是調(diào)用到了LayoutInflater.from(context).inflate(layoutId,parent,attachToRoot)
所以我們著重看最后的這個方法

    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();
        View view = tryInflatePrecompiled(resource, res, root, attachToRoot);
        if (view != null) {
            return view;
        }
        //拿到一個xml資源文件解析器
        XmlResourceParser parser = res.getLayout(resource);
        try {
            return inflate(parser, root, attachToRoot);
        } finally {
            parser.close();
        }
    }
  public View inflate(XmlPullParser parser, @Nullable ViewGroup root, boolean attachToRoot) {
        synchronized (mConstructorArgs) {
            .....
            //保存root
            View result = root;
            try {
                advanceToRootNode(parser);
                 //拿到保存在解析器的名字
                final String name = parser.getName();
                //判斷是不是MERGE標簽
                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");
                    }
                     //這里直接加載頁面邪驮,忽略merge標簽,直接傳root進rInflate進行加載子view
                    rInflate(parser, root, inflaterContext, attrs, false);
                } else {
                    //通過標簽來獲取view
                    final View temp = createViewFromTag(root, name, inflaterContext, attrs);
                    ViewGroup.LayoutParams params = null;
                    if (root != null) {
                        //temp設(shè)置布局參數(shù)
                        params = root.generateLayoutParams(attrs);
                        if (!attachToRoot) {
                            temp.setLayoutParams(params);
                        }
                    }
                    //把temp當做root傳進去rInflateChildren
                    rInflateChildren(parser, temp, attrs, true);
                    if (root != null && attachToRoot) {
                        ////把temp添加到root中并設(shè)置布局參數(shù)
                        root.addView(temp, params);
                    }
                    if (root == null || !attachToRoot) {
                        result = temp;
                    }
                }

            } catch (XmlPullParserException e) {
              ...
            } finally {
               ...
            }
            return result;
        }
    }
    //通過標簽創(chuàng)建View
    View createViewFromTag(View parent, String name, Context context, AttributeSet attrs,
            boolean ignoreThemeAttr) {
        if (name.equals("view")) {
            name = attrs.getAttributeValue(null, "class");
        }
        ....

        try {
            View view = tryCreateView(parent, name, context, attrs);

            if (view == null) {
                final Object lastContext = mConstructorArgs[0];
                mConstructorArgs[0] = context;
                try {
                     // 判斷是不是自定義View莫辨,自定義View在布局文件中com.hc.BannerView是個全類名,
                    if (-1 == name.indexOf('.')) {
                        view = onCreateView(context, parent, name, attrs);
                    } else {
                        view = createView(context, name, null, attrs);
                    }
                } finally {
                    mConstructorArgs[0] = lastContext;
                }
            }
            return view;
        } catch (InflateException e) {
           ....
        }
    }
    public final View tryCreateView(@Nullable View parent, @NonNull String name,
        @NonNull Context context,
        @NonNull AttributeSet attrs) {
        if (name.equals(TAG_1995)) {
            return new BlinkLayout(context, attrs);
        }

        View view;
        if (mFactory2 != null) {
            view = mFactory2.onCreateView(parent, name, context, attrs);
        } else if (mFactory != null) {
            view = mFactory.onCreateView(name, context, attrs);
        } else {
            view = null;
        }

        if (view == null && mPrivateFactory != null) {
            view = mPrivateFactory.onCreateView(parent, name, context, attrs);
        }

        return view;
    }
    public final View createView(@NonNull Context viewContext, @NonNull String name,
            @Nullable String prefix, @Nullable AttributeSet attrs)
            throws ClassNotFoundException, InflateException {
           // 做一些反射的性能優(yōu)化

        try {
            // 先從緩存中拿毅访,這是沒拿到的情況
            if (constructor == null) {
                // Class not found in the cache, see if it's real, and try to add it
                // 加載 clazz
                clazz = mContext.getClassLoader().loadClass(
                        prefix != null ? (prefix + name) : name).asSubclass(View.class);
                
                // 創(chuàng)建View的構(gòu)造函數(shù)
                constructor = clazz.getConstructor(mConstructorSignature);
                constructor.setAccessible(true);
                // 加入緩存集合集合
                sConstructorMap.put(name, constructor);
            } else {
                
            }
            // 通過反射創(chuàng)建View
            final View view = constructor.newInstance(args);
            return view;

        } catch (NoSuchMethodException e) {
             ......
        }
    }

看到這就應(yīng)該明白了沮榜,創(chuàng)建View 對象的時候,當root喻粹!=null蟆融,最后的參數(shù)attachToRoot要傳入true

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市守呜,隨后出現(xiàn)的幾起案子型酥,更是在濱河造成了極大的恐慌,老刑警劉巖查乒,帶你破解...
    沈念sama閱讀 218,204評論 6 506
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件弥喉,死亡現(xiàn)場離奇詭異,居然都是意外死亡玛迄,警方通過查閱死者的電腦和手機档桃,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 93,091評論 3 395
  • 文/潘曉璐 我一進店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來憔晒,“玉大人藻肄,你說我怎么就攤上這事【艿#” “怎么了嘹屯?”我有些...
    開封第一講書人閱讀 164,548評論 0 354
  • 文/不壞的土叔 我叫張陵,是天一觀的道長从撼。 經(jīng)常有香客問我州弟,道長,這世上最難降的妖魔是什么低零? 我笑而不...
    開封第一講書人閱讀 58,657評論 1 293
  • 正文 為了忘掉前任婆翔,我火速辦了婚禮,結(jié)果婚禮上掏婶,老公的妹妹穿的比我還像新娘啃奴。我一直安慰自己,他們只是感情好雄妥,可當我...
    茶點故事閱讀 67,689評論 6 392
  • 文/花漫 我一把揭開白布最蕾。 她就那樣靜靜地躺著,像睡著了一般老厌。 火紅的嫁衣襯著肌膚如雪瘟则。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 51,554評論 1 305
  • 那天枝秤,我揣著相機與錄音醋拧,去河邊找鬼。 笑死淀弹,一個胖子當著我的面吹牛丹壕,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播垦页,決...
    沈念sama閱讀 40,302評論 3 418
  • 文/蒼蘭香墨 我猛地睜開眼雀费,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了痊焊?” 一聲冷哼從身側(cè)響起盏袄,我...
    開封第一講書人閱讀 39,216評論 0 276
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎薄啥,沒想到半個月后辕羽,有當?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 45,661評論 1 314
  • 正文 獨居荒郊野嶺守林人離奇死亡垄惧,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 37,851評論 3 336
  • 正文 我和宋清朗相戀三年刁愿,在試婚紗的時候發(fā)現(xiàn)自己被綠了。 大學時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片到逊。...
    茶點故事閱讀 39,977評論 1 348
  • 序言:一個原本活蹦亂跳的男人離奇死亡铣口,死狀恐怖滤钱,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情脑题,我是刑警寧澤件缸,帶...
    沈念sama閱讀 35,697評論 5 347
  • 正文 年R本政府宣布,位于F島的核電站叔遂,受9級特大地震影響他炊,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜已艰,卻給世界環(huán)境...
    茶點故事閱讀 41,306評論 3 330
  • 文/蒙蒙 一痊末、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧哩掺,春花似錦凿叠、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,898評論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至誊薄,卻和暖如春履恩,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背呢蔫。 一陣腳步聲響...
    開封第一講書人閱讀 33,019評論 1 270
  • 我被黑心中介騙來泰國打工切心, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留,地道東北人片吊。 一個月前我還...
    沈念sama閱讀 48,138評論 3 370
  • 正文 我出身青樓绽昏,卻偏偏與公主長得像,于是被迫代替她去往敵國和親俏脊。 傳聞我的和親對象是個殘疾皇子全谤,可洞房花燭夜當晚...
    茶點故事閱讀 44,927評論 2 355