乐趣区

Activity系列博客5篇

目录介绍

  • 01. 前沿介绍
  • 02.handleLaunchActivity
  • 03.performLaunchActivity
  • 04.activity.attach
  • 05.Activity 的 onCreate 方法
  • 06.setContentView
  • 07. 关于一点总结

Activity 一系列深度博客,挖掘 activity 从启动过程,到布局创建,以及绘制的过程。

  • 02.Activity 基础介绍
  • 03.Activity 启动流程
  • 04.Activity 布局创建
  • 05.Activity 布局绘制
  • 06.Activity 探坑分析

吕诗禹想换个工作,渴望同行内推

  • 个人信息

    • 姓名:吕诗禹
    • 邮箱:17801164348@163.com
    • 微信:13940574490
    • GitHub:https://github.com/yangchong211
    • 目前工作情况:在职状态
    • 工作年限:4 年
    • 工作地点:北京
  • 感谢同行朋友,如果可以,可以直接电话联系或者微信联系!

01. 前沿介绍

  • 大家都知道在 Android 体系中 Activity 扮演了一个界面展示的角色,这也是它与 android 中另外一个很重要的组件 Service 最大的不同,但是这个展示的界面的功能是 Activity 直接控制的么?界面的布局文件是如何加载到内存并被 Activity 管理的?android 中的 View 是一个怎样的概念?加载到内存中的布局文件是如何绘制出来的?
  • 其实 Activity 对界面布局的管理是都是通过 Window 对象来实现的,Window 对象,顾名思义就是一个窗口对象,而 Activity 从用户角度就是一个个的窗口实例,因此不难想象每个 Activity 中都对应着一个 Window 对象,而这个 Window 对象就是负责加载显示界面的。至于 window 对象是如何展示不同的界面的,那是通过定义不同的 View 组件实现不同的界面展示。

02.handleLaunchActivity

  • 当 ActivityManagerService 接收到启动 Activity 的请求之后会通过 IApplicationThread 进程间通讯告知 ApplicationThread 并执行 handleLauncherActivity 方法,这里可以看一下其具体实现:

    • 可以发现这里的 handleLauncherActivity 方法内部调用了 performLaunchActivity 方法。
    private void handleLaunchActivity(ActivityClientRecord r, Intent customIntent) {
        // If we are getting ready to gc after going to the background, well
        // we are back active so skip it.
        unscheduleGcIdler();
        mSomeActivitiesChanged = true;
    
        if (r.profilerInfo != null) {mProfiler.setProfiler(r.profilerInfo);
            mProfiler.startProfiling();}
    
        // Make sure we are running with the most recent config.
        handleConfigurationChanged(null, null);
    
        if (localLOGV) Slog.v(TAG, "Handling launch of" + r);
    
        // Initialize before creating the activity
        WindowManagerGlobal.initialize();
    
        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);
    
            if (!r.activity.mFinished && r.startsNotResumed) {
                // The activity manager actually wants this one to start out
                // paused, because it needs to be visible but isn't in the
                // foreground.  We accomplish this by going through the
                // normal startup (because activities expect to go through
                // onResume() the first time they run, before their window
                // is displayed), and then pausing it.  However, in this case
                // we do -not- need to do the full pause cycle (of freezing
                // and such) because the activity manager assumes it can just
                // retain the current state it has.
                try {
                    r.activity.mCalled = false;
                    mInstrumentation.callActivityOnPause(r.activity);
                    // We need to keep around the original state, in case
                    // we need to be created again.  But we only do this
                    // for pre-Honeycomb apps, which always save their state
                    // when pausing, so we can not have them save their state
                    // when restarting from a paused state.  For HC and later,
                    // we want to (and can) let the state be saved as the normal
                    // part of stopping the activity.
                    if (r.isPreHoneycomb()) {r.state = oldState;}
                    if (!r.activity.mCalled) {
                        throw new SuperNotCalledException("Activity" + r.intent.getComponent().toShortString() +
                            "did not call through to super.onPause()");
                    }
    
                } catch (SuperNotCalledException e) {throw e;} catch (Exception e) {if (!mInstrumentation.onException(r.activity, e)) {
                        throw new RuntimeException(
                                "Unable to pause activity"
                                + r.intent.getComponent().toShortString()
                                + ":" + e.toString(), e);
                    }
                }
                r.paused = true;
            }
        } else {
            // If there was an error, for any reason, tell the activity
            // manager to stop us.
            try {ActivityManagerNative.getDefault()
                    .finishActivity(r.token, Activity.RESULT_CANCELED, null, false);
            } catch (RemoteException ex) {// Ignore}
        }
    }

03.performLaunchActivity

  • 这个方法也是具体启动 Activity 的方法,我们来看一下它的具体实现逻辑:

    • 从代码中可以看到这里是通过反射的机制创建的 Activity,并调用了 Activity 的 attach 方法,那么这里的 attach 方法是做什么的呢?
    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);
            StrictMode.incrementExpectedActivityCount(activity.getClass());
            r.intent.setExtrasClassLoader(cl);
            r.intent.prepareToEnterProcess();
            if (r.state != null) {r.state.setClassLoader(cl);
            }
        } catch (Exception e) {if (!mInstrumentation.onException(activity, e)) {
                throw new RuntimeException(
                    "Unable to instantiate activity" + component
                    + ":" + e.toString(), e);
            }
        }
        
        ...
            Application app = r.packageInfo.makeApplication(false, mInstrumentation);
    
            if (localLOGV) Slog.v(TAG, "Performing launch of" + r);
            if (localLOGV) Slog.v(
                    TAG, r + ": app=" + app
                    + ", appName=" + app.getPackageName()
                    + ", pkg=" + r.packageInfo.getPackageName()
                    + ", comp=" + r.intent.getComponent().toShortString()
                    + ", dir=" + r.packageInfo.getAppDir());
    
            if (activity != null) {Context appContext = createBaseContextForActivity(r, activity);
                CharSequence title = r.activityInfo.loadLabel(appContext.getPackageManager());
                Configuration config = new Configuration(mCompatConfiguration);
                if (DEBUG_CONFIGURATION) Slog.v(TAG, "Launching activity"
                        + r.activityInfo.name + "with config" + config);
                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);
    
        ...
    
        return activity;
    }

04.activity.attach

  • 我们继续来看一下 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) {attachBaseContext(context);
    
        mFragments.attachHost(null /*parent*/);
    
        mWindow = new PhoneWindow(this);
        mWindow.setCallback(this);
        mWindow.setOnWindowDismissedCallback(this);
        mWindow.getLayoutInflater().setPrivateFactory(this);
        if (info.softInputMode != WindowManager.LayoutParams.SOFT_INPUT_STATE_UNSPECIFIED) {mWindow.setSoftInputMode(info.softInputMode);
        }
        if (info.uiOptions != 0) {mWindow.setUiOptions(info.uiOptions);
        }
        mUiThread = Thread.currentThread();
    
        mMainThread = aThread;
        mInstrumentation = instr;
        mToken = token;
        mIdent = ident;
        mApplication = application;
        mIntent = intent;
        mReferrer = referrer;
        mComponent = intent.getComponent();
        mActivityInfo = info;
        mTitle = title;
        mParent = parent;
        mEmbeddedID = id;
        mLastNonConfigurationInstances = lastNonConfigurationInstances;
        if (voiceInteractor != null) {if (lastNonConfigurationInstances != null) {mVoiceInteractor = lastNonConfigurationInstances.voiceInteractor;} else {
                mVoiceInteractor = new VoiceInteractor(voiceInteractor, this, this,
                        Looper.myLooper());
            }
        }
    
        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;
    }
  • 可以看到在 attach 方法这里初始化了一些 Activity 的成员变量,主要是 mWindow 对象,并且 mWindow 的成员实例是 PhoneWindow 实例,这样也从侧面说明了一个 Activity 对应着一个 Window 对象。除了 window 对象还初始化了一些 Activity 的其他成员变量,这里不再做讨论,继续回到我们的 performLaunchActivity 方法,在调用了 Activity 的 attach 方法之后又调用了:

    mInstrumentation.callActivityOnCreate(activity, r.state);
  • 这里的 mInstrumentation 是类 Instrumentation,每个应用进程对应着一个 Instrumentation 和一个 ActivityThread,Instrumentation 就是具体操作 Activity 回调其生命周期方法的,我们这里看一下它的 callActivityOnCreate 方法的实现:

    public void callActivityOnCreate(Activity activity, Bundle icicle) {prePerformCreate(activity);
        activity.performCreate(icicle);
        postPerformCreate(activity);
    }
  • 这里代码比较简洁,preOerformCreate 方法和 postPerformCreate 方法我们这里暂时不管,主要的执行逻辑是调用了 activity.performCreate 方法,我们来看一下 Activity 的 performCreate 方法的实现:

    final void performCreate(Bundle icicle) {onCreate(icicle);
        mActivityTransitionState.readState(icicle);
        performCreateCommon();}
  • 原来 onCreate 的生命周期方法是在这里回调的,其实这里的逻辑在前面几篇文章中有讲述,也可以参考前面的文章。

05.Activity 的 onCreate 方法

  • 至此就回调到了我们 Activity 的 onCreate 方法,大家平时在重写 onCreate 方法的时候,怎么加载布局文件的呢?这里看一下我们的 onCreate 方法的典型写法:

    @Override
    protected void onCreate(Bundle savedInstanceState) {super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
    }
  • 无论我们怎么变化,我们的 onCreate 方法一般都是会调用这两句话的吧?那么这里的两段代码分辨是什么含义呢?我们首先看一下 super.onCreate 方法的实现逻辑,由于我们的 Activity 类继承与 Activity,所以这里的 super.onCreate 方法,就是调用的 Activity.onCreate 方法,好吧,既然这样我们来看一下 Activity 的 onCreate 方法:

    protected void onCreate(@Nullable Bundle savedInstanceState) {if (DEBUG_LIFECYCLE) Slog.v(TAG, "onCreate" + this + ":" + savedInstanceState);
        if (mLastNonConfigurationInstances != null) {mFragments.restoreLoaderNonConfig(mLastNonConfigurationInstances.loaders);
        }
        if (mActivityInfo.parentActivityName != null) {if (mActionBar == null) {mEnableDefaultActionBarUp = true;} else {mActionBar.setDefaultDisplayHomeAsUpEnabled(true);
            }
        }
        if (savedInstanceState != null) {Parcelable p = savedInstanceState.getParcelable(FRAGMENTS_TAG);
            mFragments.restoreAllState(p, mLastNonConfigurationInstances != null
                    ? mLastNonConfigurationInstances.fragments : null);
        }
        mFragments.dispatchCreate();
        getApplication().dispatchActivityCreated(this, savedInstanceState);
        if (mVoiceInteractor != null) {mVoiceInteractor.attachActivity(this);
        }
        mCalled = true;
    }
  • 可以发现,Activity 的 onCreate 方法主要是做了一些 Acitivty 的初始化操作,那么如果我们不在自己的 Activity 调用 super.onCreate 方法呢?好吧,尝试之后,AndroidStudio 在打开的 Acitivty 的 onCreate 方法中如果不调用 super.onCreate 方法的话,会报错。。。

    FATAL EXCEPTION: main                                                              Process: com.example.aaron.helloworld, PID: 18001                                                                 android.util.SuperNotCalledException: Activity {com.example.aaron.helloworld/com.example.aaron.helloworld.SecondActivity} did not call through to super.onCreate()                                                                    at android.app.ActivityThread.performLaunchActivity(ActivityThread.java:2422)                                                                            at android.app.ActivityThread.handleLaunchActivity(ActivityThread.java:2528)                                                                               at android.app.ActivityThread.access$800(ActivityThread.java:169)                                                                              at android.app.ActivityThread$H.handleMessage(ActivityThread.java:1421)                                                                             at android.os.Handler.dispatchMessage(Handler.java:111)                                                                            at android.os.Looper.loop(Looper.java:194)                                                                           at android.app.ActivityThread.main(ActivityThread.java:5552)                                                                        at java.lang.reflect.Method.invoke(Native Method)                                                                        at java.lang.reflect.Method.invoke(Method.java:372)                                                                      at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:964)                                                                       at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:759)
  • 可以看到如果不调用 super.onCreate 方法的话,会在 Activity 的 performLaunchActivity 中报错,我们知道这里的 performLaunchActivity 方法就是我们启动 Activity 的时候回回调的方法,我们找找方法体实现中 throws 的 Exception。。。

    activity.mCalled = false;
    if (r.isPersistable()) {mInstrumentation.callActivityOnCreate(activity, r.state, r.persistentState);
    } else {mInstrumentation.callActivityOnCreate(activity, r.state);
    }
    if (!activity.mCalled) {
        throw new SuperNotCalledException("Activity" + r.intent.getComponent().toShortString() +
            "did not call through to super.onCreate()");
    }
  • 在 Activity 的 performLaunchActivity 方法中,我们在调用了 Activity 的 onCreate 方法之后会执行一个判断逻辑,若 Activity 的 mCalled 为 false,则会抛出我们刚刚捕获的异常,那么这个 mCalled 成员变量是在什么时候被赋值的呢?好吧,就是在 Activity 的 onCreate 方法赋值的,所以我们在实现自己的 Activity 的时候只有调用了 super.onCreate 方法才不会抛出这个异常,反过来说,我们实现自己的 Actiivty,那么一定要在 onCreate 方法中调用 super.onCreate 方法。

06.setContentView

  • 然后我们在看一下 onCreate 中的 setContentView 方法,这里的参数就是一个 Layout 布局文件,可以发现这里的 setContentView 方法就是 Acitivty 中的 setContentView,好吧我们来看一下 Activity 中 setContentView 的实现:

    public void setContentView(@LayoutRes int layoutResID) {getWindow().setContentView(layoutResID);
        initWindowDecorActionBar();}
  • 这里的 getWindow 方法就是获取 Acitivty 的 mWindow 成员变量,从刚刚我们在 Activity.attach 方法我们知道这里的 mWindow 的实例是 PhoneWindow,所以这里调用的其实是 PhoneWindow 的 setConentView 方法,然后我们看一下 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();
        }
    }
  • 这里的 mContentParent 对象是一个 View 对象,由于第一次 mContentParent 为空,所以执行 installerDector 方法,这里我们看一下 installerDector 方法的具体实现:

    private void installDecor() {if (mDecor == null) {mDecor = generateDecor();
            mDecor.setDescendantFocusability(ViewGroup.FOCUS_AFTER_DESCENDANTS);
            mDecor.setIsRootNamespace(true);
            if (!mInvalidatePanelMenuPosted && mInvalidatePanelMenuFeatures != 0) {mDecor.postOnAnimation(mInvalidatePanelMenuRunnable);
            }
        }
        ...
    }
  • 这里的 mDector 是一个 DectorView 对象,而 DectorView 继承与 FrameLayout,所以这里的 mDector 其实就是一个 FrameLayout 对象,并通过调用 generateDector() 方法初始化,我们继续看一下 generateDector 方法的具体实现:

    protected DecorView generateDecor() {return new DecorView(getContext(), -1);
    }
  • 就是通过 new 的方式创建了一个 DectorView 对象,然后我们继续看 installDector 方法:

    if (mContentParent == null) {mContentParent = generateLayout(mDecor);
  • 这里初始化了 mContentParent 对象,这是一个 View 对象,我们调用了 generateLayout 方法,好吧,来看一下 generateLayout 方法的具体实现:

    protected ViewGroup generateLayout(DecorView decor) {
        ...
        // Inflate the window decor.
    
        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) {// Special case for a window with only a progress bar (and title).
            // XXX Need to have a no-title version of embedded windows.
            layoutResource = R.layout.screen_progress;
            // System.out.println("Progress!");
        } else if ((features & (1 << FEATURE_CUSTOM_TITLE)) != 0) {
            // Special case for a window with a custom title.
            // If the window is floating, we need a dialog layout
            if (mIsFloating) {TypedValue res = new TypedValue();
                getContext().getTheme().resolveAttribute(R.attr.dialogCustomTitleDecorLayout, res, true);
                layoutResource = res.resourceId;
            } else {layoutResource = R.layout.screen_custom_title;}
            // XXX Remove this once action bar supports these features.
            removeFeature(FEATURE_ACTION_BAR);
        } else if ((features & (1 << FEATURE_NO_TITLE)) == 0) {
            // If no other features and not embedded, only need a title.
            // If the window is floating, we need a dialog layout
            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;}
            // System.out.println("Title!");
        } else if ((features & (1 << FEATURE_ACTION_MODE_OVERLAY)) != 0) {layoutResource = R.layout.screen_simple_overlay_action_mode;} else {
            // Embedded, so no decoration is needed.
            layoutResource = R.layout.screen_simple;
            // System.out.println("Simple!");
        }
    
        mDecor.startChanging();
    
        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);
        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;}
            mDecor.setWindowBackground(background);
    
            final Drawable frame;
            if (mFrameResource != 0) {frame = getContext().getDrawable(mFrameResource);
            } else {frame = null;}
            mDecor.setWindowFrame(frame);
    
            mDecor.setElevation(mElevation);
            mDecor.setClipToOutline(mClipToOutline);
    
            if (mTitle != null) {setTitle(mTitle);
            }
    
            if (mTitleColor == 0) {mTitleColor = mTextColor;}
            setTitleColor(mTitleColor);
        }
    
        mDecor.finishChanging();
    
        return contentParent;
    }
  • 可以发现这里就是通过调用 LayoutInflater.inflate 方法来加载布局文件到内存中,关于 LayoutInflater.inflater 是如何加载布局文件的,并且,通过对代码的分析,我们发现 PhoneWindow 中的几个成员变量:mDector,mContentRoot,mContentParent 的关系

mDector –> mContentRoot –> mContentParent(包含)

- 并且我们来看一下典型的布局文件:```
<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>
```
- 这里就是整个 Activity 加载的跟布局文件:screen_simple.xml,其中 ViewStub 对应着 Activity 中的 titleBar 而这里的 FrameLayout 里面主要用于填充内容。
  • 然后我们具体看一下 LayoutInflater.inflater 方法:

    public View inflate(@LayoutRes int resource, @Nullable ViewGroup root) {return inflate(resource, root, root != null);
    }
  • 这里调用了 inflate 的重载方法。。。

    public View inflate(XmlPullParser parser, @Nullable ViewGroup root, boolean attachToRoot) {synchronized (mConstructorArgs) {Trace.traceBegin(Trace.TRACE_TAG_VIEW, "inflate");
    
            final Context inflaterContext = mContext;
            final AttributeSet attrs = Xml.asAttributeSet(parser);
            Context lastContext = (Context) mConstructorArgs[0];
            mConstructorArgs[0] = inflaterContext;
            View result = root;
    
            try {
                // Look for the root node.
                int type;
                while ((type = parser.next()) != XmlPullParser.START_TAG &&
                        type != XmlPullParser.END_DOCUMENT) {// Empty}
    
                if (type != XmlPullParser.START_TAG) {throw new InflateException(parser.getPositionDescription()
            }

            final String name = parser.getName();
            
            if (DEBUG) {System.out.println("**************************");
                System.out.println("Creating root view:"
                        + name);
                System.out.println("**************************");
            }

            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");
                }

                rInflate(parser, root, inflaterContext, attrs, false);
            } else {
                // Temp is the root view that was found in the xml
                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);
                    }
                    // Create layout params that match root, if supplied
                    params = root.generateLayoutParams(attrs);
                    if (!attachToRoot) {
                        // Set the layout params for temp if we are not
                        // attaching. (If we are, we use addView, below)
                        temp.setLayoutParams(params);
                    }
                }

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

                // Inflate all children under temp against its context.
                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) {InflateException ex = new InflateException(e.getMessage());
            ex.initCause(e);
            throw ex;
        } catch (Exception e) {
            InflateException ex = new InflateException(parser.getPositionDescription()
                            + ":" + e.getMessage());
            ex.initCause(e);
            throw ex;
        } finally {
            // Don't retain static reference on context.
            mConstructorArgs[0] = lastContext;
            mConstructorArgs[1] = null;
        }

        Trace.traceEnd(Trace.TRACE_TAG_VIEW);

        return result;
    }
}
```
  • 通过分析源码,不难发现,主要是通过循环解析 xml 文件并将信息解析到内存 View 对象,布局文件中定义的一个个组件都被顺序的解析到了内存中并被父子 View 的形式组织起来,这样通过给定的一个 root View 就可以将整个布局文件中定义的组件全部解析。分析完解析布局文件,回到我们的 setContentVIew 方法,在调用了 installDector 方法之后,又调用了:

    mLayoutInflater.inflate(layoutResID, mContentParent);
  • 这个方法的含义就是将我们传递的客户端的 layoutId 对应的布局文件作为 mContentParent 的子 View 加载到内存中,这样我们的 layoutId 作为 mContentParent 的子 View,而 mContentParent 又是 mContentRoot 的子 View,mContentRoot 又是 mDector 的子 View,通过 LayoutInflater 的 inflate 方法逐步加载到了内存中,而我们的 Activity 又持有自身的 PhoneWindow 的引用,这就相当于我们的 Activity 持有了我们定义的布局文件的引用,因而 Activity 的布局文件被加载到了内存中。

07. 关于一点总结

  • 总结:

    • Activity 的展示界面的特性是通过 Window 对象来控制的;
    • 每个 Activity 对象都对应这个一个 Window 对象,并且 Window 对象的初始化在启动 Activity 的时候完成,在执行 Activity 的 onCreate 方法之前;
    • 每个 Window 对象内部都存在一个 FrameLayout 类型的 mDector 对象,它是 Acitivty 界面的 root view;
    • Activity 中的 window 对象的实例是 PhoneWindow 对象,PhoneWindow 对象中的几个成员变量 mDector,mContentRoot,mContentParent 都是 View 组件,它们的关系是:mDector –> mContentRoot –> mContentParent –> 自定义 layoutView
    • LayoutInflater.inflate 主要用于将布局文件加载到内存 View 组件中,也可以设定加载到某一个父组件中;
    • 典型的 Activity 的 onCreate 方法中需要调用 super.onCreate 方法和 setContentView 方法,若不调用 super.onCreate 方法,执行启动该 Activity 的逻辑会报错,若不执行 setContentView 的方法,该 Activity 只会显示一个空页面。

其他介绍

01. 关于博客汇总链接

  • 1. 技术博客汇总
  • 2. 开源项目汇总
  • 3. 生活博客汇总
  • 4. 喜马拉雅音频汇总
  • 5. 其他汇总

02. 关于我的博客

  • github:https://github.com/yangchong211
  • 知乎:https://www.zhihu.com/people/…
  • 简书:http://www.jianshu.com/u/b7b2…
  • csdn:http://my.csdn.net/m0_37700275
  • 喜马拉雅听书:http://www.ximalaya.com/zhubo…
  • 开源中国:https://my.oschina.net/zbj161…
  • 泡在网上的日子:http://www.jcodecraeer.com/me…
  • 邮箱:yangchong211@163.com
  • 阿里云博客:https://yq.aliyun.com/users/a… 239.headeruserinfo.3.dT4bcV
  • segmentfault 头条:https://segmentfault.com/u/xi…
  • 掘金:https://juejin.im/user/593943…

开源地址:https://github.com/yangchong211

退出移动版