setContent方法源碼解析

本文源碼基于6.0分析绘趋。
首先看一下Activity中的setContentView颤陶。

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

我們可以看到實際上調(diào)用的是getWindow().setContentView(layoutResID),我們知道getWindow()獲得的Window對象就是PhoneWindow對象陷遮,所以實際上調(diào)用的是PhoneWindow中的setContentView(layoutResID)滓走。
PhoneWindow.java

    @Override
    public void setContentView(int layoutResID) {
        //如果mContentParent為null,說明窗口還沒有加載內(nèi)容帽馋,則初始化DecorView搅方。
        if (mContentParent == null) {
            installDecor();
        } else if (!hasFeature(FEATURE_CONTENT_TRANSITIONS)) {
            //FEATURE_CONTENT_TRANSITIONS標(biāo)志位,這個是標(biāo)記當(dāng)前內(nèi)容加載有沒有使用過度動畫绽族,也就是轉(zhuǎn)場動畫姨涡。
            //否則mContentParent不為null,說明已經(jīng)加載過了吧慢,并且沒有使用轉(zhuǎn)場動畫涛漂,那么remove所以view。
            mContentParent.removeAllViews();
        }
        //如果使用轉(zhuǎn)場動畫检诗,則調(diào)用transitionTo方法
        if (hasFeature(FEATURE_CONTENT_TRANSITIONS)) {
            final Scene newScene = Scene.getSceneForLayout(mContentParent, layoutResID,
                    getContext());
            transitionTo(newScene);
        } else {
            //否則將我們的資源文件通過LayoutInflater對象轉(zhuǎn)換為View樹匈仗,并且添加至mContentParent視圖中。
            mLayoutInflater.inflate(layoutResID, mContentParent);
        }
        mContentParent.requestApplyInsets();
        final Callback cb = getCallback();
        if (cb != null && !isDestroyed()) {
            cb.onContentChanged();
        }
    }

接下來我們詳細分析以下setContentView方法中調(diào)用的方法逢慌。
先看一下installDecor()方法悠轩。

    private void installDecor() {
        if (mDecor == null) {
            //如果mDecor為null,調(diào)用generateDecor()創(chuàng)建
            mDecor = generateDecor();
            mDecor.setDescendantFocusability(ViewGroup.FOCUS_AFTER_DESCENDANTS);
            mDecor.setIsRootNamespace(true);
            if (!mInvalidatePanelMenuPosted && mInvalidatePanelMenuFeatures != 0) {
                mDecor.postOnAnimation(mInvalidatePanelMenuRunnable);
            }
        }
        //mContentParent為 null說明DecorView并未加載到mContentParent中涕癣。
        if (mContentParent == null) {
            //調(diào)用generateLayout方法給mContentParent賦值
            mContentParent = generateLayout(mDecor);
            mDecor.makeOptionalFitsSystemWindows();

            final DecorContentParent decorContentParent = (DecorContentParent) mDecor.findViewById(
                    R.id.decor_content_parent);

            //設(shè)置TitleView(TextView),Backdroud等資源,設(shè)置有無轉(zhuǎn)場動畫以及轉(zhuǎn)場動畫的種類
            ......
    }
    protected DecorView generateDecor() {
        return new DecorView(getContext(), -1);
    }
protected ViewGroup generateLayout(DecorView decor) {
        TypedArray a = getWindowStyle();
        ......
        //設(shè)置style為Window_windowNoTitle或者Window_windowActionBar
        if (a.getBoolean(R.styleable.Window_windowNoTitle, false)) {
            requestFeature(FEATURE_NO_TITLE);
        } else if (a.getBoolean(R.styleable.Window_windowActionBar, false)) {
            // Don't allow an action bar if there is no title.
            requestFeature(FEATURE_ACTION_BAR);
        }
        //省略一些style資源的判斷
        ......
        //添加布局到DecorView哗蜈,前面我們通過generateDecor() 創(chuàng)建DecorView,現(xiàn)在DecorView里面還是null的坠韩,下面通過設(shè)置的Feature來創(chuàng)建相應(yīng)的布局距潘。
        //舉個例子,如果我在setContentView之前調(diào)用了requestWindowFeature(Window.FEATURE_NO_TITLE)只搁,這里則會通過getLocalFeatures來獲取你設(shè)置的feature音比,進而加載對應(yīng)的布局,
        //此時是加載沒有標(biāo)題欄的主題氢惋,這也就是為什么我們在代碼中設(shè)置Theme或者requesetFeature()的時候必須在setContentView之前的原因.
        int layoutResource;
        int features = getLocalFeatures();
        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;
            }
            removeFeature(FEATURE_ACTION_BAR);
        } else if ((features & ((1 << FEATURE_PROGRESS) | (1 << FEATURE_INDETERMINATE_PROGRESS))) != 0
                && (features & (1 << FEATURE_ACTION_BAR)) == 0) {
            layoutResource = R.layout.screen_progress;
        } else if ((features & (1 << FEATURE_CUSTOM_TITLE)) != 0) {
            if (mIsFloating) {
                TypedValue res = new TypedValue();
                getContext().getTheme().resolveAttribute(
                        R.attr.dialogCustomTitleDecorLayout, res, true);
                layoutResource = res.resourceId;
            } else {
                layoutResource = R.layout.screen_custom_title;
            }
            removeFeature(FEATURE_ACTION_BAR);
        } else if ((features & (1 << FEATURE_NO_TITLE)) == 0) {
            if (mIsFloating) {
                TypedValue res = new TypedValue();
                getContext().getTheme().resolveAttribute(
                        R.attr.dialogTitleDecorLayout, res, true);
                layoutResource = res.resourceId;
            } else if ((features & (1 << FEATURE_ACTION_BAR)) != 0) {
                layoutResource = a.getResourceId(
                        R.styleable.Window_windowActionBarFullscreenDecorLayout,
                        R.layout.screen_action_bar);
            } else {
                layoutResource = R.layout.screen_title;
            }
        } else if ((features & (1 << FEATURE_ACTION_MODE_OVERLAY)) != 0) {
            layoutResource = R.layout.screen_simple_overlay_action_mode;
        } else {
            layoutResource = R.layout.screen_simple;
        }

        mDecor.startChanging();
        //將相應(yīng)的布局解析成View并添加到decorView中洞翩。
        View in = mLayoutInflater.inflate(layoutResource, null);
        decor.addView(in, new ViewGroup.LayoutParams(MATCH_PARENT, MATCH_PARENT));
        mContentRoot = (ViewGroup) in;

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

        mDecor.finishChanging();
        //generateLayout()的返回是contentParent稽犁,而它的獲取則是ViewGroup contentParent = (ViewGroup)findViewById(ID_ANDROID_CONTENT);
        //DecorView是頂級View,它可以標(biāo)示整個屏幕骚亿,其中包含狀態(tài)欄已亥,導(dǎo)航欄,內(nèi)容區(qū)等等来屠。這里的mContentParent指的是屏幕顯示的內(nèi)容區(qū)虑椎,而我們設(shè)置的activity_main.xml布局則是mContentParent里面的一個子元素。
        return contentParent;
    }

screen_simple.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>

我們再次回到PhoneWindow類的setContentVeiw()方法中俱笛,通過調(diào)用installDecor()方法捆姜,我們已經(jīng)創(chuàng)建了DecorView,并獲取到了mContentParent迎膜,接著就是將我們setContentView的內(nèi)容添加到mContentParent中泥技,也就是mLayoutInflater.inflate(layoutResID, mContentParent);

@Override
    public void setContentView(int layoutResID) {
        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();
        }
    }

我們先看一下最后幾句代碼

       final Callback cb = getCallback();
          if (cb != null && !isDestroyed()) {
             cb.onContentChanged();
        }

這個CallBack是Window內(nèi)部的一個接口,Window中實現(xiàn)了setCallback和getCallback方法磕仅。setCallback()方法是在Activity中被調(diào)用的珊豹。

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) {

        mWindow = new PhoneWindow(this);
}

而Activity本身也實現(xiàn)了Window.Callback接口,我們看一下Activity中實現(xiàn)的onContentChanged()方法宽涌。

public void onContentChanged() {
}

該方法是一個空方法平夜,而當(dāng)Activity的布局改動時,即setContentView()或者addContentView()方法執(zhí)行完畢時就會調(diào)用該方法卸亮。我們?nèi)粘i_發(fā)時可以利用起來忽妒。
接下來我們分析一下LayoutInflater.inflat()方法,看是如何將我們自己的布局加載上去的兼贸。
到目前為止段直,通過setContentView方法,創(chuàng)建了DecorView和加載了我們自己的布局溶诞,但是View還是不可見的鸯檬,因為我們只是加載了布局,并沒有對View進行任何的測量螺垢、布局喧务、繪制工作。
在View進行測量流程之前枉圃,還要進行一個步驟功茴,那就是把DecorView添加至window中。
要說把DecorView添加到Window中孽亲,那我們就要從Activity的啟動說起了坎穿,Activity啟動的過程中必經(jīng)的一步—ActivityThread類中的的handleLaunchActivity()方法。

private void handleLaunchActivity(ActivityClientRecord r, Intent customIntent) {
        ......
        Activity a = performLaunchActivity(r, customIntent);

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

            ......
        } else {
            ......
        }
    }

我們來看一下performLaunchActivity(r, customIntent)。

private Activity performLaunchActivity(ActivityClientRecord r, Intent customIntent) {
        ......
        Activity activity = null;
        try {
            java.lang.ClassLoader cl = r.packageInfo.getClassLoader();
            activity = mInstrumentation.newActivity(
                    cl, component.getClassName(), r.intent);
            }
        ......
        try {
            Application app = r.packageInfo.makeApplication(false, mInstrumentation);
            ......

            if (activity != null) {
                Context appContext = createBaseContextForActivity(r, activity);
                CharSequence title = r.activityInfo.loadLabel(appContext.getPackageManager());
                Configuration config = new Configuration(mCompatConfiguration);
                ......

                if (customIntent != null) {
                    activity.mIntent = customIntent;
                }
                r.lastNonConfigurationInstances = null;
                activity.mStartedActivity = false;
                int theme = r.activityInfo.getThemeResource();
                if (theme != 0) {
                    activity.setTheme(theme);
                }

                activity.mCalled = false;
                if (r.isPersistable()) {
                    mInstrumentation.callActivityOnCreate(activity, r.state, r.persistentState);    
                } else {
                    mInstrumentation.callActivityOnCreate(activity, r.state);
                }
                ......
            }
            r.paused = true;

            mActivities.put(r.token, r);

        }

        return activity;
    }

調(diào)用了Instrumentation類的newActivity方法玲昧。Instrumentation類是一個全權(quán)負責(zé)Activity生命周期的類栖茉。

public Activity newActivity(Class<?> clazz, Context context, 
            IBinder token, Application application, Intent intent, ActivityInfo info, 
            CharSequence title, Activity parent, String id,
            Object lastNonConfigurationInstance) throws InstantiationException, 
            IllegalAccessException {
        Activity activity = (Activity)clazz.newInstance();
        ActivityThread aThread = null;
        activity.attach(context, aThread, this, token, 0, application, intent,
                info, title, parent, id,
                (Activity.NonConfigurationInstances)lastNonConfigurationInstance,
                new Configuration(), null, null);
        return activity;
    }

通過反射創(chuàng)建了一個新的Activity實例,在該方法中調(diào)用了Activity類中的attach()方法,回到performLaunchActivity方法中孵延,接下來會調(diào)用Instrumentation類的callActivityOnCreate方法吕漂。

public void callActivityOnCreate(Activity activity, Bundle icicle) {
        prePerformCreate(activity);
        activity.performCreate(icicle);
        postPerformCreate(activity);
    }

調(diào)用了Activity中的performCreate方法。

final void performCreate(Bundle icicle) {
        onCreate(icicle);
        mActivityTransitionState.readState(icicle);
        performCreateCommon();
    }

調(diào)用了onCreate方法隙袁,我們知道onCreate方法是Activity的第一個生命周期方法痰娱,在這里我們調(diào)用了setContentView()弃榨,整個performLaunchActivity()函數(shù)就會返回一個已經(jīng)執(zhí)行完onCreat()和setContetnView()的activity對象菩收,之前我們說setContentView()執(zhí)行完之后,View此時還是不可見的,要等DecorView添加至window中,然后觸發(fā)ViewRootImpl#performTraversals方法開始View的繪制,測量等工作后才會可見鲸睛。
回到ActivityThread中handleLaunchActivity方法繼續(xù)看娜饵,在執(zhí)行完performLaunchActivity方法后會執(zhí)行handleResumeActivity方法。

final void handleResumeActivity(IBinder token,
            boolean clearHide, boolean isForward, boolean reallyResume) {
        ......
        ActivityClientRecord r = performResumeActivity(token, clearHide);

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

            ......
            if (r.window == null && !a.mFinished && willBeVisible) {
                r.window = r.activity.getWindow();
                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 (a.mVisibleFromClient) {
                    a.mWindowAdded = true;
                    wm.addView(decor, l);
                }

            } else if (!willBeVisible) {
                if (localLOGV) Slog.v(
                    TAG, "Launch " + r + " mStartedActivity set");
                r.hideForNow = true;
            }
            cleanUpPendingRemoveWindows(r);

            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();
                        wm.updateViewLayout(decor, l);
                    }
                }
                r.activity.mVisibleFromServer = true;
                mNumVisibleActivities++;
                if (r.activity.mVisibleFromClient) {
                    r.activity.makeVisible();
                }
            }

            ......
        } else {
            try {
                ActivityManagerNative.getDefault()
                    .finishActivity(token, Activity.RESULT_CANCELED, null, false);
            } catch (RemoteException ex) {
            }
        }
    }

首先執(zhí)行了performResumeActivity()方法

public final ActivityClientRecord performResumeActivity(IBinder token,
            boolean clearHide) {
        ActivityClientRecord r = mActivities.get(token);
        if (r != null && !r.activity.mFinished) {
            if (clearHide) {
                r.hideForNow = false;
                r.activity.mStartedActivity = false;
            }
            try {
                r.activity.onStateNotSaved();
                r.activity.mFragments.noteStateNotSaved();
                if (r.pendingIntents != null) {
                    deliverNewIntents(r, r.pendingIntents);
                    r.pendingIntents = null;
                }
                if (r.pendingResults != null) {
                    deliverResults(r, r.pendingResults);
                    r.pendingResults = null;
                }
                r.activity.performResume();
                ......
            } catch (Exception e) {
                ......
            }
        }
        return r;
    }

可看到執(zhí)行了r.activity.performResume();即Activity中的performResume()方法官辈。

final void performResume() {
        ......
        mInstrumentation.callActivityOnResume(this);
        ......

        onPostResume();
        if (!mCalled) {
            throw new SuperNotCalledException(
                "Activity " + mComponent.toShortString() +
                " did not call through to super.onPostResume()");
        }
    }

調(diào)用了Instrumentation類中的callActivityOnResume()方法.

public void callActivityOnResume(Activity activity) {
        activity.mResumed = true;
        activity.onResume();
        
       ......
    }

可以看到最終調(diào)用了Activity的生命周期方法onResume()箱舞。
繼續(xù)看handleResumeActivity()方法中,performResumeActivity()方法執(zhí)行完畢后拳亿,也就是執(zhí)行到onResume()方法晴股,此時內(nèi)容仍然是不可見的,并且還沒有執(zhí)行View的測量肺魁、擺放电湘、繪制等操作,因此此時獲取View的寬高仍然是0鹅经。
handleResumeActivity()方法中寂呛。

    r.window = r.activity.getWindow();
    View decor = r.window.getDecorView();   
    decor.setVisibility(View.INVISIBLE);
    ViewManager wm = a.getWindowManager();

r.activity.getWindow()獲得當(dāng)前Activity的PhoneWindow對象.
View decor = r.window.getDecorView();獲得當(dāng)前phoneWindow內(nèi)部類DecorView對象.
decor.setVisibility(View.INVISIBLE);剛獲得這個DecorView的時候先設(shè)置為不可見.
ViewManager wm = a.getWindowManager();創(chuàng)建一個ViewManager對象.

public WindowManager getWindowManager() {
        return mWindowManager;
    }

mWindowManager唯一賦值的地方在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) {
        ......
        mWindowManager = mWindow.getWindowManager();
    }

我們繼續(xù)到Window中看一下getWindowManager()的實現(xiàn)瘾晃。

public WindowManager getWindowManager() {
        return mWindowManager;
    }

Window類中mWindowManager唯一賦值的地方在setWindowManager方法中贷痪。

public void setWindowManager(WindowManager wm, IBinder appToken, String appName,
            boolean hardwareAccelerated) {
        ......
        mWindowManager = ((WindowManagerImpl)wm).createLocalWindowManager(this);
    }

調(diào)用了WindowManagerImpl中的createLocalWindowManager()方法

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

可以看到,最終實際上是new了一個WindowManagerImpl對象蹦误。
也就是說ViewManager wm = a.getWindowManager();返回的wm是一個WindowManagerImpl類的實例劫拢。
繼續(xù)回到handleResumeActivity方法中。

 WindowManager.LayoutParams l = r.window.getAttributes();
 a.mDecor = decor;
 l.type = WindowManager.LayoutParams.TYPE_BASE_APPLICATION;
 l.softInputMode |= forwardBit;
 if (a.mVisibleFromClient) {
     a.mWindowAdded = true;
     wm.addView(decor, l);
 }

獲取Window的LayoutParams屬性强胰,將mWindowAdded屬性置為true舱沧,然后將DecorView添加到wm中,即添加到Window中哪廓,此時才是真正將View添加到Window中狗唉。
看一下addView方法的實現(xiàn)。

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

可以看到實際上調(diào)用了WindowManagerGlobal的addView方法涡真。

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 {
            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) {
            ......

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

            view.setLayoutParams(wparams);

            mViews.add(view);
            mRoots.add(root);
            mParams.add(wparams);
        }

        try {
            root.setView(view, wparams, panelParentView);
        } catch (RuntimeException e) {
            synchronized (mLock) {
                final int index = findViewLocked(view, false);
                if (index >= 0) {
                    removeViewLocked(index, true);
                }
            }
            throw e;
        }
    }

可以看到最終還是調(diào)用了ViewRootImpl的setView方法分俯。

public void setView(View view, WindowManager.LayoutParams attrs, View panelParentView) {
        synchronized (this) {
            if (mView == null) {
                mView = view;

                ......
                mAdded = true;
                int res; /* = WindowManagerImpl.ADD_OKAY; */
                requestLayout();
                if ((mWindowAttributes.inputFeatures
                        & WindowManager.LayoutParams.INPUT_FEATURE_NO_INPUT_CHANNEL) == 0) {
                    mInputChannel = new InputChannel();
                }
                try {
                    mOrigWindowType = mWindowAttributes.type;
                    mAttachInfo.mRecomputeGlobalAttributes = true;
                    collectViewAttributes();
                    res = mWindowSession.addToDisplay(mWindow, mSeq, mWindowAttributes,
                            getHostVisibility(), mDisplay.getDisplayId(),
                            mAttachInfo.mContentInsets, mAttachInfo.mStableInsets,
                            mAttachInfo.mOutsets, mInputChannel);
                } catch (RemoteException e) {
                    mAdded = false;
                    ......
                } finally {
                    if (restore) {
                        attrs.restore();
                    }
                }
                ......
            }
        }
    }

傳進來的view就是DecorView肾筐,首先將mAdded置為true,表明已經(jīng)成功添加了DecorView缸剪,然后調(diào)用requestLayout()方法重繪界面吗铐。

    @Override
    public void requestLayout() {
        if (!mHandlingLayoutInLayoutRequest) {
            checkThread();
            mLayoutRequested = true;
            scheduleTraversals();
        }
    }

首先調(diào)用了checkThread()方法去檢查是否在當(dāng)前線程,這也是為什么我們在子線程中操作View會報異常的原因杏节。接下來調(diào)用scheduleTraversals()方法唬渗。

void scheduleTraversals() {
        if (!mTraversalScheduled) {
            mTraversalScheduled = true;
            mTraversalBarrier = mHandler.getLooper().getQueue().postSyncBarrier();
            mChoreographer.postCallback(
                    Choreographer.CALLBACK_TRAVERSAL, mTraversalRunnable, null);
            if (!mUnbufferedInputDispatch) {
                scheduleConsumeBatchedInput();
            }
            notifyRendererOfFramePending();
            pokeDrawLockIfNeeded();
        }
    }

調(diào)用到了mChoreographer.postCallback(Choreographer.CALLBACK_TRAVERSAL, mTraversalRunnable, null),我們看一下mTraversalRunnable這個Runnable對象奋渔。

final class TraversalRunnable implements Runnable {
        @Override
        public void run() {
            doTraversal();
        }
    }

繼續(xù)看doTraversal()方法的實現(xiàn)镊逝。

 void doTraversal() {
        if (mTraversalScheduled) {
            mTraversalScheduled = false;
            mHandler.getLooper().getQueue().removeSyncBarrier(mTraversalBarrier);

            performTraversals();

            if (mProfile) {
                Debug.stopMethodTracing();
                mProfile = false;
            }
        }
    }

里面有一個很重要的方法,performTraversals()它就是我們View真正開始測量嫉鲸、擺放撑蒜、繪制的入口了。該方法中會依次調(diào)用performMeasure玄渗、performLayout座菠、performDraw()操作,具體的View繪制流程我們單獨拿一篇文章做講解藤树,這里不做深究浴滴。
好我們繼續(xù)回到handleResumeActivity方法中,剛才是分析的WindowManagerImpl的addView方法岁钓,最終會執(zhí)行到performTraversals()來執(zhí)行View的繪制升略。
我們知道此時DecorView已經(jīng)被添加到了Window中了,但是在添加之前DecorView被設(shè)置成不可見了甜紫,decor.setVisibility(View.INVISIBLE);
因此此時還是不可見狀態(tài)降宅,我們繼續(xù)看handleResumeActivity方法,最后我們可以看到這樣一段代碼:

if (r.activity.mVisibleFromClient) {
      r.activity.makeVisible();
}

進入到Activity中看一下makeVisible()方法的實現(xiàn)囚霸。

   void makeVisible() {
        if (!mWindowAdded) {
            ViewManager wm = getWindowManager();
            wm.addView(mDecor, getWindow().getAttributes());
            mWindowAdded = true;
        }
        mDecor.setVisibility(View.VISIBLE);
    }

我們看到最終調(diào)用mDecor.setVisibility(View.VISIBLE)來將DecorView設(shè)置成可見腰根,界面也就顯示出來了。
從Activity的生命周期的角度來看,也就是onResume()執(zhí)行完之后,DecorView才開始attach給WindowManager從而顯示出來拓型。
總結(jié)一下:
Activity在onCreate之前調(diào)用attach方法额嘿,在attach方法中會創(chuàng)建window對象。window對象創(chuàng)建時并沒有創(chuàng)建Decor對象劣挫。用戶在Activity中調(diào)用setContentView,然后調(diào)用window的setContentView册养,這時會檢查DecorView是否存在,如果不存在則創(chuàng)建DecorView對象压固,然后把用戶自己的View 添加到DecorView中球拦。
至此,我們就分析完了setContentView方法的流程。
下一篇將分析View的繪制流程坎炼,將從performTraversals()方法開始分析愧膀。

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市谣光,隨后出現(xiàn)的幾起案子檩淋,更是在濱河造成了極大的恐慌,老刑警劉巖萄金,帶你破解...
    沈念sama閱讀 219,427評論 6 508
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件蟀悦,死亡現(xiàn)場離奇詭異,居然都是意外死亡氧敢,警方通過查閱死者的電腦和手機日戈,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 93,551評論 3 395
  • 文/潘曉璐 我一進店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來福稳,“玉大人涎拉,你說我怎么就攤上這事〉脑玻” “怎么了?”我有些...
    開封第一講書人閱讀 165,747評論 0 356
  • 文/不壞的土叔 我叫張陵半火,是天一觀的道長越妈。 經(jīng)常有香客問我,道長钮糖,這世上最難降的妖魔是什么梅掠? 我笑而不...
    開封第一講書人閱讀 58,939評論 1 295
  • 正文 為了忘掉前任,我火速辦了婚禮店归,結(jié)果婚禮上阎抒,老公的妹妹穿的比我還像新娘。我一直安慰自己消痛,他們只是感情好且叁,可當(dāng)我...
    茶點故事閱讀 67,955評論 6 392
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著秩伞,像睡著了一般逞带。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上纱新,一...
    開封第一講書人閱讀 51,737評論 1 305
  • 那天展氓,我揣著相機與錄音,去河邊找鬼脸爱。 笑死遇汞,一個胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播空入,決...
    沈念sama閱讀 40,448評論 3 420
  • 文/蒼蘭香墨 我猛地睜開眼教寂,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了酪耕?” 一聲冷哼從身側(cè)響起,我...
    開封第一講書人閱讀 39,352評論 0 276
  • 序言:老撾萬榮一對情侶失蹤轨淌,失蹤者是張志新(化名)和其女友劉穎迂烁,沒想到半個月后,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體递鹉,經(jīng)...
    沈念sama閱讀 45,834評論 1 317
  • 正文 獨居荒郊野嶺守林人離奇死亡盟步,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 37,992評論 3 338
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發(fā)現(xiàn)自己被綠了躏结。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片却盘。...
    茶點故事閱讀 40,133評論 1 351
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖媳拴,靈堂內(nèi)的尸體忽然破棺而出黄橘,到底是詐尸還是另有隱情,我是刑警寧澤屈溉,帶...
    沈念sama閱讀 35,815評論 5 346
  • 正文 年R本政府宣布塞关,位于F島的核電站,受9級特大地震影響子巾,放射性物質(zhì)發(fā)生泄漏帆赢。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點故事閱讀 41,477評論 3 331
  • 文/蒙蒙 一线梗、第九天 我趴在偏房一處隱蔽的房頂上張望椰于。 院中可真熱鬧,春花似錦仪搔、人聲如沸瘾婿。這莊子的主人今日做“春日...
    開封第一講書人閱讀 32,022評論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽憋他。三九已至,卻和暖如春髓削,著一層夾襖步出監(jiān)牢的瞬間竹挡,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 33,147評論 1 272
  • 我被黑心中介騙來泰國打工立膛, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留揪罕,地道東北人梯码。 一個月前我還...
    沈念sama閱讀 48,398評論 3 373
  • 正文 我出身青樓,卻偏偏與公主長得像好啰,于是被迫代替她去往敵國和親轩娶。 傳聞我的和親對象是個殘疾皇子乳讥,可洞房花燭夜當(dāng)晚...
    茶點故事閱讀 45,077評論 2 355

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