Activity系列博客5篇

目錄介紹

  • 01.前沿介紹
  • 02.handleLaunchActivity
  • 03.performLaunchActivity
  • 04.activity.attach
  • 05.Activity的onCreate方法
  • 06.setContentView
  • 07.關於一點總結

Activity一系列深度博客,挖掘activity從啓動過程,到佈局創建,以及繪製的過程。

呂詩禹想換個工作,渴望同行內推

  • 個人信息

  • 感謝同行朋友,如果可以,可以直接電話聯繫或者微信聯繫!

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()
                            + ": No start tag found!");
                }
    
                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.關於博客彙總鏈接

02.關於我的博客

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

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章