美文网首页
setContentView流程分析

setContentView流程分析

作者: 我要离开浪浪山 | 来源:发表于2023-04-14 15:48 被阅读0次

一、前言:

二、 Activity的流程

1、Activity的流程

  • 1、ActivityThread类中调用performLaunchActivity,创建Activity;
  • 2、Activity类中调用activity.attach方法;
  • 3、attach方法中 new PhoneWindow()对象;
  • 4、attach方法中会给mInstrumentation赋值;
  • 5、ActivityThread类中mInstrumentation.callActivityOnCreate

1、ActivityThread类中调用performLaunchActivity,创建Activity;

   private Activity performLaunchActivity(ActivityClientRecord r, Intent customIntent) {
        //......
        Activity activity = null;
        try {
            java.lang.ClassLoader cl = appContext.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);
            }
        }

        return activity;
    }

2、Activity类中调用activity.attach方法;

//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, IBinder assistToken) {
        attachBaseContext(context);

        mFragments.attachHost(null /*parent*/);

        mWindow = new PhoneWindow(this, window, activityConfigCallback);
        

5、ActivityThread类中mInstrumentation.callActivityOnCreate

  if (r.isPersistable()) {
                    mInstrumentation.callActivityOnCreate(activity, r.state, r.persistentState);
                } else {
                    mInstrumentation.callActivityOnCreate(activity, r.state);
                }

2、打开Activity.java方法,查看setContentView

 public void setContentView(@LayoutRes int layoutResID) {
       // getWindow()获取的是PhoneWindow对象,去加载布局
        getWindow().setContentView(layoutResID);
        initWindowDecorActionBar();
    }

这里面是PhoneWindow调用setcontentview

3、Activity的 attach什么时候调用?

Activity的attach方法在Activity被创建时调用,即在onCreate方法中。在attach方法中,Activity会与它的Window进行关联,并且会创建一个Context对象,用于与应用程序的其他组件进行交互。在attach方法中,还会进行一些初始化操作,例如设置Activity的主题获取Intent传递的数据等。

注意:可以看到创建的Window是PhoneWindow

三、Activity的setcontent流程

PhoneWindow.setContentView --- 主要目的 创建 DecorView 拿到 Content


1acb3cd08798250c5a7a27725f28de9.png

1、流程步骤、

  • 1、Activity的setcontentView中通过PhoneWindow去加载布局;
  • 2、PhoneWindow中的installDecor()方法 创建 mDecor(也就是DecorView );
  • 3、PhoneWindow中的installDecor()方法 创建mContentParent (也就是ViewGroup );
  • 4、mContentParent = generateLayout(mDecor);中加载布局R.layout.screen_simple
  • 5、mDecor.onResourcesLoaded(mLayoutInflater, layoutResource);
  • 6、ViewGroup contentParent = (ViewGroup)findViewById(ID_ANDROID_CONTENT);

2、PhoneWindow哪些类会创建

  • 1.Activity
  • 2.Dialog
  • 3.PopupWindow
  • 4.Toast

3、PhoneWindow的构造方法

//构造方法1
  public PhoneWindow(Context context) {
        super(context);
        mLayoutInflater = LayoutInflater.from(context);
        mRenderShadowsInCompositor = Settings.Global.getInt(context.getContentResolver(),
                DEVELOPMENT_RENDER_SHADOWS_IN_COMPOSITOR, 1) != 0;
    }
//构造方法2
//mDecor 这个就是DecorView;
private DecorView mDecor;
 public PhoneWindow(Context context, Window preservedWindow,
            ActivityConfigCallback activityConfigCallback) {
        this(context);
        // Only main activity windows use decor context, all the other windows depend on whatever
        // context that was given to them.
        mUseDecorContext = true;
        if (preservedWindow != null) {
            mDecor = (DecorView) preservedWindow.getDecorView();
            mElevation = preservedWindow.getElevation();
            mLoadElevation = false;
            mForceDecorInstall = true;
            // If we're preserving window, carry over the app token from the preserved
            // window, as we'll be skipping the addView in handleResumeActivity(), and
            // the token will not be updated as for a new window.
            getAttributes().token = preservedWindow.getAttributes().token;
        }
        // Even though the device doesn't support picture-in-picture mode,
        // an user can force using it through developer options.
        boolean forceResizable = Settings.Global.getInt(context.getContentResolver(),
                DEVELOPMENT_FORCE_RESIZABLE_ACTIVITIES, 0) != 0;
        mSupportsPictureInPicture = forceResizable || context.getPackageManager().hasSystemFeature(
                PackageManager.FEATURE_PICTURE_IN_PICTURE);
        mActivityConfigCallback = activityConfigCallback;
    }

3、phoneWindow的setContentView

//installDecor:装饰对象
  @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();
        }
        mContentParentExplicitlySet = true;
    }

4、installDecor()方法

//1、创建mDecor 方法
//2、创建mContentParent 方法
private DecorView mDecor;
ViewGroup mContentParent;
  private void installDecor() {
        mForceDecorInstall = false;
        if (mDecor == null) {
            mDecor = generateDecor(-1);
            mDecor.setDescendantFocusability(ViewGroup.FOCUS_AFTER_DESCENDANTS);
            mDecor.setIsRootNamespace(true);
            if (!mInvalidatePanelMenuPosted && mInvalidatePanelMenuFeatures != 0) {
                mDecor.postOnAnimation(mInvalidatePanelMenuRunnable);
            }
        } else {
            mDecor.setWindow(this);
        }
        if (mContentParent == null) {
            mContentParent = generateLayout(mDecor);

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

5、activity中setcontentview流程

1.installDecor()->mDecor = generateDecor(-1)生成DecorView->mContentParent =generateLayout(mDecor);加载系统布局到Decorview,mDecor.onResourcesLoaded(mLayoutInflater, layoutResource);
--> ViewGroup contentParent = (ViewGroup)findViewById(ID_ANDROID_CONTENT);里面的@android:id/content赋值给mContentParent

2.mLayoutInflater.inflate(layoutResID, mContentParent);

四、继承 AppCompatActivity 的流程

1、加载流程

1、AppCompatActivity 类中 AppCompatDelegate.setContentView 加载布局
2、setContentView 中调用ensureSubDecor()方法
3、ensureSubDecor()方法中创建mSubDecor(也就是ViewGroup,它的方法是createSubDecor())
4、createSubDecor()方法中创建了 ensureWindow(),也就是Activity中的PhoneWindow;
5、ensureWindow()方法,mWindow.getDecorView()接下来和Activity一样了,

cf1da55ace79779eb684525c9f7e0b2.png

2、setContentView

    @Override
    public void setContentView(@LayoutRes int layoutResID) {
        initViewTreeOwners();
        getDelegate().setContentView(layoutResID);
    }

    @Override
    public void setContentView(int resId) {
        ensureSubDecor();
        ViewGroup contentParent = mSubDecor.findViewById(android.R.id.content);
        contentParent.removeAllViews();
        LayoutInflater.from(mContext).inflate(resId, contentParent);
        mAppCompatWindowCallback.getWrapped().onContentChanged();
    }

3、ensureSubDecor方法

ViewGroup mSubDecor;
 private void ensureSubDecor() {
        if (!mSubDecorInstalled) {
            mSubDecor = createSubDecor();

            // If a title was set before we installed the decor, propagate it now
            CharSequence title = getTitle();
            if (!TextUtils.isEmpty(title)) {
                if (mDecorContentParent != null) {
                    mDecorContentParent.setWindowTitle(title);
                } else if (peekSupportActionBar() != null) {
                    peekSupportActionBar().setWindowTitle(title);
                } else if (mTitleView != null) {
                    mTitleView.setText(title);
                }
            }

            applyFixedSizeWindow();

            onSubDecorInstalled(mSubDecor);

            mSubDecorInstalled = true
            PanelFeatureState st = getPanelState(FEATURE_OPTIONS_PANEL, false);
            if (!mDestroyed && (st == null || st.menu == null)) {
                invalidatePanelMenu(FEATURE_SUPPORT_ACTION_BAR);
            }
        }
    }

4、createSubDecor方法

   private ViewGroup createSubDecor() {
        TypedArray a = mContext.obtainStyledAttributes(R.styleable.AppCompatTheme);
         //调用创建window方法,和Activity方法一样了
         ensureWindow();
        mWindow.getDecorView();

        final LayoutInflater inflater = LayoutInflater.from(mContext);
        ViewGroup subDecor = null;
}

5、AppCompatActivity的setcontentview流程

1.AppCompatDelegate.setContentView ->ensureSubDecor()->createSubDecor里面调用ensureWindow(); // 从Activity 那PhoneWindow,mWindow.getDecorView()里面调用installDecor()
-> final ViewGroup windowContentView = (ViewGroup) mWindow.findViewById(android.R.id.content);
-> windowContentView.setId(View.NO_ID); // 将原始的 content id 置为 NO_ID
-> contentView.setId(android.R.id.content); // subDecerView R.id.action_bar_activity_content -> 置为 content
-> mWindow.setContentView(subDecor); //
-> ViewGroup contentParent = mSubDecor.findViewById(android.R.id.content);

相关文章

网友评论

      本文标题:setContentView流程分析

      本文链接:https://www.haomeiwen.com/subject/ofzzddtx.html