【Android源码】setContentView流程

1 Activity中的setContentView

我们都知道Activity里,setContentView是我们用来加载布局的,那么它里面的源码是怎么样的呢?先看下父类Activity里面的setContentView方法体

    /**
     * Set the activity content from a layout resource.  The resource will be
     * inflated, adding all top-level views to the activity.
     *从一个布局资源中设置Activity内容。资源将被加载,所有的顶级视图都会被加载到Activity。(个人理解就是从资源文件中加载视图到Activity中)
     * @param layoutResID Resource ID to be inflated.
     *要被加载的资源ID
     * @see #setContentView(android.view.View)
     * @see #setContentView(android.view.View, android.view.ViewGroup.LayoutParams)
     */
    public void setContentView(@LayoutRes int layoutResID) {
        getWindow().setContentView(layoutResID);//#1
        initWindowDecorActionBar();//#2
    }

从源码看,Activity中的setContentView中只有两行代码,我们先分析#1行代码。它首先调用了getWindow()方法。

2 Activity中getWindow方法

    /**
     * Retrieve the current {@link android.view.Window} for the activity.
     * This can be used to directly access parts of the Window API that
     * are not available through Activity/Screen.
     *不知道英文说的啥,反正返回的就是当前Activity的Window对象。
     * @return Window The current window, or null if the activity is not visual.
     * 返回当前Window对象,如果当前Activity不可见则返回空
     */
    public Window getWindow() {
        return mWindow;
    }

可以看到,getWindow()方法获取当前Activity的Window对象。那么,我们看看这个mWindow在哪个位置被初始化了呢?

3 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) {
    ...
    mWindow = new PhoneWindow(this, window, activityConfigCallback);
    ...
}

在Activity的attach方法中有上面一句代码,该代码是将mWindow实例化为一个PhoneWindow对象。那么,继续深究,attach又是在什么时候调用的呢?其实我们在启动Activity时,它首先会调用ActivityThread中的main方法,main方法如下所示:

4 ActivityThread中的main方法

public static void main(String[] args) {
        Trace.traceBegin(Trace.TRACE_TAG_ACTIVITY_MANAGER, "ActivityThreadMain");
        SamplingProfilerIntegration.start();

        // CloseGuard defaults to true and can be quite spammy.  We
        // disable it here, but selectively enable it later (via
        // StrictMode) on debug builds, but using DropBox, not logs.
        CloseGuard.setEnabled(false);

        Environment.initForCurrentUser();

        // Set the reporter for event logging in libcore
        EventLogger.setReporter(new EventLoggingReporter());

        // Make sure TrustedCertificateStore looks in the right place for CA certificates
        final File configDir = Environment.getUserConfigDirectory(UserHandle.myUserId());
        TrustedCertificateStore.setDefaultUserDirectory(configDir);

        Process.setArgV0("");

        Looper.prepareMainLooper();//#1

        ActivityThread thread = new ActivityThread();//#2
        thread.attach(false);//#3

        if (sMainThreadHandler == null) {//#4
            sMainThreadHandler = thread.getHandler();//#5
        }

        if (false) {
            Looper.myLooper().setMessageLogging(new
                    LogPrinter(Log.DEBUG, "ActivityThread"));
        }

        // End of event ActivityThreadMain.
        Trace.traceEnd(Trace.TRACE_TAG_ACTIVITY_MANAGER);
        Looper.loop();//#6

        throw new RuntimeException("Main thread loop unexpectedly exited");
    }

如上所示,第#1行和第#6行形成了一个消息循环,在第#4和#5行,初始化了一个Handler对象,getHandler方法源码如下:

5 ActivityThread中的Handler

    final H mH = new H();
    
    final Handler getHandler() {
        return mH;
    }

H是一个继承Handler的子类,在H类中的handleMessage方法中,有个msg.what=LAUNCH_ACTIVITY的一个case,如下面源码所示:

    public void handleMessage(Message msg) {
            if (DEBUG_MESSAGES) Slog.v(TAG, ">>> handling: " + codeToString(msg.what));
            switch (msg.what) {
                case LAUNCH_ACTIVITY: {
                    Trace.traceBegin(Trace.TRACE_TAG_ACTIVITY_MANAGER, "activityStart");
                    final ActivityClientRecord r = (ActivityClientRecord) msg.obj;

                    r.packageInfo = getPackageInfoNoCheck(
                            r.activityInfo.applicationInfo, r.compatInfo);
                    handleLaunchActivity(r, null, "LAUNCH_ACTIVITY");
                    Trace.traceEnd(Trace.TRACE_TAG_ACTIVITY_MANAGER);
                } break;
                ...
             }
        }

当启动Activity时,就会调用这个case,然后调用handleLaunchActivity方法,在handleLaunchActivity的源码中,调用了一个方法performLaunchActivity。handleLaunchActivity源码如下所示:

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

performLaunchActivity源码如下所示:

private Activity performLaunchActivity(ActivityClientRecord r, Intent customIntent) {
    ...
    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, r.configCallback);
    ...
}

performLaunchActivity调用了Activity的attach方法。所以mWindow是在Activity启动时在attach中创建的。接着看PhoneWindow中的setContentView

6 PhoneWindow中setContentView

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);//#1
        }
        mContentParent.requestApplyInsets();
        final Callback cb = getCallback();
        if (cb != null && !isDestroyed()) {
            cb.onContentChanged();
        }
        mContentParentExplicitlySet = true;
    }

第一次加载时,mContentParent为null,会调用installDecor方法进行初始化(包括DecorView和mContentParent初始化),然后将setContentView里的View加载到mContentParent里面去,如上述代码中的第#1行。

installDecor方法的源码如下所示,

private DecorView mDecor;

private ViewGroup mContentParent;

private ViewGroup mContentRoot;

private void installDecor() {
    if (mDecor == null) {
        mDecor = generateDecor();
        mDecor.setDescendantFocusability(ViewGroup.FOCUS_AFTER_DESCENDANTS);
        mDecor.setIsRootNamespace(true);
        if (!mInvalidatePanelMenuPosted && mInvalidatePanelMenuFeatures != 0) {
            mDecor.postOnAnimation(mInvalidatePanelMenuRunnable);
        }
    }
    if (mContentParent == null) {
        mContentParent = generateLayout(mDecor);//#1

        // Set up decor part of UI to ignore fitsSystemWindows if appropriate.
        mDecor.makeOptionalFitsSystemWindows();
        //....
    }
}

mDecor是DecorView类对象,而DecorView继承自FrameLayout,generateDecor()方法就是初始化创建一个空的FrameLayout。来看generateLayout()方法是怎样初始化我们需要的mContentParent的:

protected ViewGroup generateLayout(DecorView decor) {  
    ...
    ViewGroup contentParent = (ViewGroup)findViewById(ID_ANDROID_CONTENT);
    ...
    return contentParent;
}

7 总结

Activity启动流程(代码流程版)

(1)调用ActivityThread.main方法;(2)创建Handler对象;(3)H.handleMessage里处理LAUNCH_ACTIVITY的case;(4)调用handleLaunchActivity;(5)在handleLaunchActivity中调用performLaunchActivity(6)performLaunchActivity中调用Activity的attach方法;(7)在attach方法中创建PhoneWindow对象;(8)在Activity的setContentView中获取到PhoneWindow对象并调用PhoneWindow中的setContentView;(9)在PhoneWindow中的setContentView中创建DecorView和mContentParent对象,并将setContentView加载的view添加到mContentParent中。

你可能感兴趣的:(【Android源码】setContentView流程)