安卓Activity的setContentView()流程分析

目录

  • 前言
  • 一、Activity的视图加载过程
    • 1.1 视图结构
    • 1.2 流程分析
      • 1.2.1 Activity.java -->setContentView()
      • 1.2.2 Activity.java -->getWindow()
      • 1.2.3 PhoneWindow.java -->setContentView()
      • 1.2.4 PhoneWindow.java --->installDecor()
        • 1.2.4.1 PhoneWindow.java --->installDecor() --> mDecor = generateDecor(-1);
        • 1.2.4.2 installDecor() --> mContentParent = generateLayout(mDecor);
        • 1.2.4.3 DecorView.java 将R.layout.screen_simple添加到DecorView(FrameLayout)里
        • 1.2.4.4 随后又将screen_simple(这里举例此布局)里的id为content的framelayout拿到
      • 1.2.5 PhoneWindow.java -->setContentView(int layoutResID)
  • 二、AppCompatActivity的视图加载过程
    • 2.1 视图结构
    • 2.2 流程分析
      • 2.2.1 AppCompatActivity.java --setContentView(int layoutResID)
      • 2.2.2 AppCompatDelegate.java --> create(Activity activity, AppCompatCallback callback)
      • 2.2.3 AppCompatDelegateImpl.java -->setContentView(int resId)
      • 2.2.4 AppCompatDelegateImpl.java -->ensureSubDecor()
      • 2.2.5 AppCompatDelegateImpl.java --> createSubDecor()
        • 2.2.5.1 AppCompatDelegateImpl.java -->ensureWindow()
        • 2.2.5.2 AppCompatDelegateImpl.java -->mWindow.getDecorView()
        • 2.2.5.3 继续看createSubDecor()方法,在执行完mWindow.getDecorView()方法后,对subDecor的操作。
        • 2.2.5.4 ID替换
        • 2.2.5.5 mWindow.setContentView(subDecor)
      • 2.2.6 退出createSubDecor(),退出ensureSubDecor(),关于布局加载的代码已经完毕
  • 三、总结


前言

常用的Activity有Activity和AppCompatActivity,分别梳理一下这两个类的setContentView()流程

一、Activity的视图加载过程

1.1 视图结构

先放视图结构:
​​​​在这里插入图片描述

1.2 流程分析

接下来看一下流程

1.2.1 Activity.java -->setContentView()

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

1.2.2 Activity.java -->getWindow()

返回mWindow对象,此处的mWindow对象实例化为PhoneWindow,实例化过程涉及Activity的启动流程:

PhoneWindow的实例化过程:
在Activity中attach方法内,PhoneWindow以New的方式被实例化成mWindow对象

    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) {attachBaseContext(context);mFragments.attachHost(null /*parent*/);//PhoneWindow 在这里被实例化出来了mWindow = new PhoneWindow(this, window, activityConfigCallback);mWindow.setWindowControllerCallback(this);mWindow.setCallback(this);mWindow.setOnWindowDismissedCallback(this);mWindow.getLayoutInflater().setPrivateFactory(this);....../*并且在以下代码中,我们可以发现到Activity的相关生命周期的调用*/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()");}r.activity = activity;r.stopped = true;if (!r.activity.mFinished) {activity.performStart();r.stopped = false;}if (!r.activity.mFinished) {if (r.isPersistable()) {if (r.state != null || r.persistentState != null) {mInstrumentation.callActivityOnRestoreInstanceState(activity, r.state,r.persistentState);}} else if (r.state != null) {mInstrumentation.callActivityOnRestoreInstanceState(activity, r.state);}}if (!r.activity.mFinished) {activity.mCalled = false;if (r.isPersistable()) {mInstrumentation.callActivityOnPostCreate(activity, r.state,r.persistentState);} else {mInstrumentation.callActivityOnPostCreate(activity, r.state);}if (!activity.mCalled) {throw new SuperNotCalledException("Activity " + r.intent.getComponent().toShortString() +" did not call through to super.onPostCreate()");}}}

而attach则是在ActivityThread类中的performLaunchActivity被调用:

    private Activity performLaunchActivity(ActivityClientRecord r, Intent customIntent) {// System.out.println("##### [" + System.currentTimeMillis() + "] ActivityThread.performLaunchActivity(" + r + ")");......Activity activity = null;......try {Application app = r.packageInfo.makeApplication(false, mInstrumentation);......if (activity != null) {......appContext.setOuterContext(activity);//这里调用了Activity的attach方法,实现了PhoneWindow的初始化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);......}r.paused = true;mActivities.put(r.token, r);} catch (SuperNotCalledException e) {throw e;} catch (Exception e) {if (!mInstrumentation.onException(activity, e)) {throw new RuntimeException("Unable to start activity " + component+ ": " + e.toString(), e);}}return activity;}

So,第1个视图为PhoneWindow对象。

1.2.3 PhoneWindow.java -->setContentView()

重点关注installDecor()方法,对mContentParent进行判断如果为空使用 installDecor()方法安装Decor。

// This is the view in which the window contents are placed. It is either// mDecor itself, or a child of mDecor where the contents go.
/*
这是放置窗口内容的视图。要么mDecor本身,或者内容所在的mDecor的子级。
*/
ViewGroup mContentParent;// Whether the client has explicitly set the content view. If false and mContentParent is not null, then the content parent was set due to window preservation.
/*
客户端是否已显式设置内容视图。如果false且mContentParent不为null,则由于窗口保留而设置了内容父级。
*/
private boolean mContentParentExplicitlySet = false;
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;}

1.2.4 PhoneWindow.java —>installDecor()

installDecor:主要干了两件事:1.创建DecorView 2.拿到ContentParent
mDecor.setWindow(this); :在创建DecorView之后与PhoneWindow关联起来,DecorView为第2个视图

private DecorView mDecor;
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();final DecorContentParent decorContentParent = (DecorContentParent) mDecor.findViewById(R.id.decor_content_parent);if (decorContentParent != null) {mDecorContentParent = decorContentParent;mDecorContentParent.setWindowCallback(getCallback());if (mDecorContentParent.getTitle() == null) {mDecorContentParent.setWindowTitle(mTitle);}final int localFeatures = getLocalFeatures();for (int i = 0; i < FEATURE_MAX; i++) {if ((localFeatures & (1 << i)) != 0) {mDecorContentParent.initFeature(i);}}mDecorContentParent.setUiOptions(mUiOptions);if ((mResourcesSetFlags & FLAG_RESOURCE_SET_ICON) != 0 ||(mIconRes != 0 && !mDecorContentParent.hasIcon())) {mDecorContentParent.setIcon(mIconRes);} else if ((mResourcesSetFlags & FLAG_RESOURCE_SET_ICON) == 0 &&mIconRes == 0 && !mDecorContentParent.hasIcon()) {mDecorContentParent.setIcon(getContext().getPackageManager().getDefaultActivityIcon());mResourcesSetFlags |= FLAG_RESOURCE_SET_ICON_FALLBACK;}if ((mResourcesSetFlags & FLAG_RESOURCE_SET_LOGO) != 0 ||(mLogoRes != 0 && !mDecorContentParent.hasLogo())) {mDecorContentParent.setLogo(mLogoRes);}// Invalidate if the panel menu hasn't been created before this.// Panel menu invalidation is deferred avoiding application onCreateOptionsMenu// being called in the middle of onCreate or similar.// A pending invalidation will typically be resolved before the posted message// would run normally in order to satisfy instance state restoration.PanelFeatureState st = getPanelState(FEATURE_OPTIONS_PANEL, false);if (!isDestroyed() && (st == null || st.menu == null) && !mIsStartingWindow) {invalidatePanelMenu(FEATURE_ACTION_BAR);}} else {mTitleView = findViewById(R.id.title);if (mTitleView != null) {if ((getLocalFeatures() & (1 << FEATURE_NO_TITLE)) != 0) {final View titleContainer = findViewById(R.id.title_container);if (titleContainer != null) {titleContainer.setVisibility(View.GONE);} else {mTitleView.setVisibility(View.GONE);}mContentParent.setForeground(null);} else {mTitleView.setText(mTitle);}}}if (mDecor.getBackground() == null && mBackgroundFallbackDrawable != null) {mDecor.setBackgroundFallback(mBackgroundFallbackDrawable);}// Only inflate or create a new TransitionManager if the caller hasn't// already set a custom one.if (hasFeature(FEATURE_ACTIVITY_TRANSITIONS)) {if (mTransitionManager == null) {final int transitionRes = getWindowStyle().getResourceId(R.styleable.Window_windowContentTransitionManager,0);if (transitionRes != 0) {final TransitionInflater inflater = TransitionInflater.from(getContext());mTransitionManager = inflater.inflateTransitionManager(transitionRes,mContentParent);} else {mTransitionManager = new TransitionManager();}}mEnterTransition = getTransition(mEnterTransition, null,R.styleable.Window_windowEnterTransition);mReturnTransition = getTransition(mReturnTransition, USE_DEFAULT_TRANSITION,R.styleable.Window_windowReturnTransition);mExitTransition = getTransition(mExitTransition, null,R.styleable.Window_windowExitTransition);mReenterTransition = getTransition(mReenterTransition, USE_DEFAULT_TRANSITION,R.styleable.Window_windowReenterTransition);mSharedElementEnterTransition = getTransition(mSharedElementEnterTransition, null,R.styleable.Window_windowSharedElementEnterTransition);mSharedElementReturnTransition = getTransition(mSharedElementReturnTransition,USE_DEFAULT_TRANSITION,R.styleable.Window_windowSharedElementReturnTransition);mSharedElementExitTransition = getTransition(mSharedElementExitTransition, null,R.styleable.Window_windowSharedElementExitTransition);mSharedElementReenterTransition = getTransition(mSharedElementReenterTransition,USE_DEFAULT_TRANSITION,R.styleable.Window_windowSharedElementReenterTransition);if (mAllowEnterTransitionOverlap == null) {mAllowEnterTransitionOverlap = getWindowStyle().getBoolean(R.styleable.Window_windowAllowEnterTransitionOverlap, true);}if (mAllowReturnTransitionOverlap == null) {mAllowReturnTransitionOverlap = getWindowStyle().getBoolean(R.styleable.Window_windowAllowReturnTransitionOverlap, true);}if (mBackgroundFadeDurationMillis < 0) {mBackgroundFadeDurationMillis = getWindowStyle().getInteger(R.styleable.Window_windowTransitionBackgroundFadeDuration,DEFAULT_BACKGROUND_FADE_DURATION_MS);}if (mSharedElementsUseOverlay == null) {mSharedElementsUseOverlay = getWindowStyle().getBoolean(R.styleable.Window_windowSharedElementsUseOverlay, true);}}}}
1.2.4.1 PhoneWindow.java —>installDecor() --> mDecor = generateDecor(-1);

主要是上下文的选择以及最后对DecorView的创建,完成目标1创建DecorView

protected DecorView generateDecor(int featureId) {// System process doesn't have application context and in that case we need to directly use// the context we have. Otherwise we want the application context, so we don't cling to the// activity.Context context;if (mUseDecorContext) {Context applicationContext = getContext().getApplicationContext();if (applicationContext == null) {context = getContext();} else {context = new DecorContext(applicationContext, this);if (mTheme != -1) {context.setTheme(mTheme);}}} else {context = getContext();}return new DecorView(context, featureId, this, getAttributes());}
1.2.4.2 installDecor() --> mContentParent = generateLayout(mDecor);

通过对窗口特征的判断对layoutResource赋值xml ID,其中窗口特征默认的情况下的话会赋值到最后R.layout.screen_simple(这里举例)

随后使用关键方法

mDecor.onResourcesLoaded(mLayoutInflater, layoutResource);
ViewGroup contentParent = (ViewGroup)findViewById(ID_ANDROID_CONTENT);

protected ViewGroup generateLayout(DecorView decor) {// Apply data from current theme.TypedArray a = getWindowStyle(); //拿到窗口属性 随后在接下来根据窗口属性设置窗口特征if (false) {System.out.println("From style:");String s = "Attrs:";for (int i = 0; i < R.styleable.Window.length; i++) {s = s + " " + Integer.toHexString(R.styleable.Window[i]) + "="+ a.getString(i);}System.out.println(s);}..................     //省略窗口特征的相关部分// Inflate the window decor.int layoutResource;int features = getLocalFeatures();// System.out.println("Features: 0x" + Integer.toHexString(features));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 layoutif (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 layoutif (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();mDecor.onResourcesLoaded(mLayoutInflater, layoutResource);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);}}// Remaining setup -- of background and title -- that only applies// to top-level windows.if (getContainer() == null) {mDecor.setWindowBackground(mBackgroundDrawable);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;}
1.2.4.3 DecorView.java 将R.layout.screen_simple添加到DecorView(FrameLayout)里

这里说DecorView是FrameLayout是因为DecorView继承自FrameLayout
mdecor已经在之前的1.4.1generateDecor()实例化,通过onResourcesLoaded()方法将R.layout.screen_simple加载为root,并通过addview方法加载到mdecor的子布局下。
R.layout.screen_simple为第3个视图

void onResourcesLoaded(LayoutInflater inflater, int layoutResource) {if (mBackdropFrameRenderer != null) {loadBackgroundDrawablesIfNeeded();mBackdropFrameRenderer.onResourcesLoaded(this, mResizingBackgroundDrawable, mCaptionBackgroundDrawable,mUserCaptionBackgroundDrawable, getCurrentColor(mStatusColorViewState),getCurrentColor(mNavigationColorViewState));}mDecorCaptionView = createDecorCaptionView(inflater);final View root = inflater.inflate(layoutResource, null);if (mDecorCaptionView != null) {if (mDecorCaptionView.getParent() == null) {addView(mDecorCaptionView,new ViewGroup.LayoutParams(MATCH_PARENT, MATCH_PARENT));}mDecorCaptionView.addView(root,new ViewGroup.MarginLayoutParams(MATCH_PARENT, MATCH_PARENT));} else {// Put it below the color views.addView(root, 0, new ViewGroup.LayoutParams(MATCH_PARENT, MATCH_PARENT));}mContentRoot = (ViewGroup) root;initializeElevation();}
1.2.4.4 随后又将screen_simple(这里举例此布局)里的id为content的framelayout拿到

在 generateLayout()方法末尾将contentParent返回,此ViewGroup对象的实例就是R.layout.screen_simple里id为content的Framelayout。id为content的Framelayout为第4个视图。

ViewGroup contentParent = (ViewGroup)findViewById(ID_ANDROID_CONTENT);
    /*** The ID that the main layout in the XML layout file should have.*/public static final int ID_ANDROID_CONTENT = com.android.internal.R.id.content;

​​在这里插入图片描述

1.2.5 PhoneWindow.java -->setContentView(int layoutResID)

installDecor()方法结束之后,setContentView(int layoutResID)继续执行,在下述代码中将自己的layout布局文件填充到mContentParent中。自己的layout布局文件为第5个视图

 if (hasFeature(FEATURE_CONTENT_TRANSITIONS)) {final Scene newScene = Scene.getSceneForLayout(mContentParent, layoutResID,getContext());transitionTo(newScene);} else {mLayoutInflater.inflate(layoutResID, mContentParent);}

二、AppCompatActivity的视图加载过程

2.1 视图结构

直接上图
​​在这里插入图片描述

2.2 流程分析

2.2.1 AppCompatActivity.java --setContentView(int layoutResID)

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

initViewTreeOwners()是初始化view树的所有者,这里不多介绍。
getDelegate()方法:

 @NonNullpublic AppCompatDelegate getDelegate() {if (mDelegate == null) {mDelegate = AppCompatDelegate.create(this, this);}return mDelegate;}

2.2.2 AppCompatDelegate.java --> create(Activity activity, AppCompatCallback callback)

 public static AppCompatDelegate create(@NonNull Activity activity,@Nullable AppCompatCallback callback) {return new AppCompatDelegateImpl(activity, callback);}

2.2.3 AppCompatDelegateImpl.java -->setContentView(int resId)

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

2.2.4 AppCompatDelegateImpl.java -->ensureSubDecor()

private void ensureSubDecor() {if (!mSubDecorInstalled) {mSubDecor = createSubDecor();// If a title was set before we installed the decor, propagate it nowCharSequence 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;// Invalidate if the panel menu hasn't been created before this.// Panel menu invalidation is deferred avoiding application onCreateOptionsMenu// being called in the middle of onCreate or similar.// A pending invalidation will typically be resolved before the posted message// would run normally in order to satisfy instance state restoration.PanelFeatureState st = getPanelState(FEATURE_OPTIONS_PANEL, false);if (!mIsDestroyed && (st == null || st.menu == null)) {invalidatePanelMenu(FEATURE_SUPPORT_ACTION_BAR);}}}

2.2.5 AppCompatDelegateImpl.java --> createSubDecor()

 private ViewGroup createSubDecor() {TypedArray a = mContext.obtainStyledAttributes(R.styleable.AppCompatTheme);................//根据窗口属性设置窗口特征ensureWindow();mWindow.getDecorView();final LayoutInflater inflater = LayoutInflater.from(mContext);ViewGroup subDecor = null;if (!mWindowNoTitle) {if (mIsFloating) {// If we're floating, inflate the dialog title decorsubDecor = (ViewGroup) inflater.inflate(R.layout.abc_dialog_title_material, null);// Floating windows can never have an action bar, reset the flagsmHasActionBar = mOverlayActionBar = false;} else if (mHasActionBar) {/*** This needs some explanation. As we can not use the android:theme attribute* pre-L, we emulate it by manually creating a LayoutInflater using a* ContextThemeWrapper pointing to actionBarTheme.*/TypedValue outValue = new TypedValue();mContext.getTheme().resolveAttribute(R.attr.actionBarTheme, outValue, true);Context themedContext;if (outValue.resourceId != 0) {themedContext = new ContextThemeWrapper(mContext, outValue.resourceId);} else {themedContext = mContext;}// Now inflate the view using the themed context and set it as the content viewsubDecor = (ViewGroup) LayoutInflater.from(themedContext).inflate(R.layout.abc_screen_toolbar, null);mDecorContentParent = (DecorContentParent) subDecor.findViewById(R.id.decor_content_parent);mDecorContentParent.setWindowCallback(getWindowCallback());/*** Propagate features to DecorContentParent*/if (mOverlayActionBar) {mDecorContentParent.initFeature(FEATURE_SUPPORT_ACTION_BAR_OVERLAY);}if (mFeatureProgress) {mDecorContentParent.initFeature(Window.FEATURE_PROGRESS);}if (mFeatureIndeterminateProgress) {mDecorContentParent.initFeature(Window.FEATURE_INDETERMINATE_PROGRESS);}}} else {if (mOverlayActionMode) {subDecor = (ViewGroup) inflater.inflate(R.layout.abc_screen_simple_overlay_action_mode, null);} else {subDecor = (ViewGroup) inflater.inflate(R.layout.abc_screen_simple, null);}}if (subDecor == null) {throw new IllegalArgumentException("AppCompat does not support the current theme features: { "+ "windowActionBar: " + mHasActionBar+ ", windowActionBarOverlay: "+ mOverlayActionBar+ ", android:windowIsFloating: " + mIsFloating+ ", windowActionModeOverlay: " + mOverlayActionMode+ ", windowNoTitle: " + mWindowNoTitle+ " }");}if (Build.VERSION.SDK_INT >= 21) {// If we're running on L or above, we can rely on ViewCompat's// setOnApplyWindowInsetsListenerViewCompat.setOnApplyWindowInsetsListener(subDecor, new OnApplyWindowInsetsListener() {@Overridepublic WindowInsetsCompat onApplyWindowInsets(View v,WindowInsetsCompat insets) {final int top = insets.getSystemWindowInsetTop();final int newTop = updateStatusGuard(insets, null);if (top != newTop) {insets = insets.replaceSystemWindowInsets(insets.getSystemWindowInsetLeft(),newTop,insets.getSystemWindowInsetRight(),insets.getSystemWindowInsetBottom());}// Now apply the insets on our viewreturn ViewCompat.onApplyWindowInsets(v, insets);}});} else if (subDecor instanceof FitWindowsViewGroup) {// Else, we need to use our own FitWindowsViewGroup handling((FitWindowsViewGroup) subDecor).setOnFitSystemWindowsListener(new FitWindowsViewGroup.OnFitSystemWindowsListener() {@Overridepublic void onFitSystemWindows(Rect insets) {insets.top = updateStatusGuard(null, insets);}});}if (mDecorContentParent == null) {mTitleView = (TextView) subDecor.findViewById(R.id.title);}// Make the decor optionally fit system windows, like the window's decorViewUtils.makeOptionalFitsSystemWindows(subDecor);final ContentFrameLayout contentView = (ContentFrameLayout) subDecor.findViewById(R.id.action_bar_activity_content);final ViewGroup windowContentView = (ViewGroup) mWindow.findViewById(android.R.id.content);if (windowContentView != null) {// There might be Views already added to the Window's content view so we need to// migrate them to our content viewwhile (windowContentView.getChildCount() > 0) {final View child = windowContentView.getChildAt(0);windowContentView.removeViewAt(0);contentView.addView(child);}// Change our content FrameLayout to use the android.R.id.content id.// Useful for fragments.windowContentView.setId(View.NO_ID);contentView.setId(android.R.id.content);// The decorContent may have a foreground drawable set (windowContentOverlay).// Remove this as we handle it ourselvesif (windowContentView instanceof FrameLayout) {((FrameLayout) windowContentView).setForeground(null);}}// Now set the Window's content view with the decormWindow.setContentView(subDecor);contentView.setAttachListener(new ContentFrameLayout.OnAttachListener() {@Overridepublic void onAttachedFromWindow() {}@Overridepublic void onDetachedFromWindow() {dismissPopups();}});return subDecor;}
2.2.5.1 AppCompatDelegateImpl.java -->ensureWindow()

主要还是获取Window的实例对象PhoneWindow,涉及Activity的启动流程,在此不作过多介绍。

第1个视图:PhoneWindow

private void ensureWindow() {// We lazily fetch the Window for Activities, to allow DayNight to apply in// attachBaseContextif (mWindow == null && mHost instanceof Activity) {attachToWindow(((Activity) mHost).getWindow());}if (mWindow == null) {throw new IllegalStateException("We have not been given a Window");}}
 private void attachToWindow(@NonNull Window window) {if (mWindow != null) {throw new IllegalStateException("AppCompat has already installed itself into the Window");}final Window.Callback callback = window.getCallback();if (callback instanceof AppCompatWindowCallback) {throw new IllegalStateException("AppCompat has already installed itself into the Window");}mAppCompatWindowCallback = new AppCompatWindowCallback(callback);// Now install the new callbackwindow.setCallback(mAppCompatWindowCallback);final TintTypedArray a = TintTypedArray.obtainStyledAttributes(mContext, null, sWindowBackgroundStyleable);final Drawable winBg = a.getDrawableIfKnown(0);if (winBg != null) {// Now set the background drawablewindow.setBackgroundDrawable(winBg);}a.recycle();mWindow = window;}
public Window getWindow() {return mWindow;}
2.2.5.2 AppCompatDelegateImpl.java -->mWindow.getDecorView()

–>PhoneWindow.java -->getDecorView()

DecorView关联到Window上,第2个视图:DecorView
R.layout.screen_simple为第3个视图:DecorView
id为content的Framelayout为第4个视图:DecorView
和Activity的setContentView()流程类似

@Overridepublic final @NonNull View getDecorView() {if (mDecor == null || mForceDecorInstall) {installDecor();}return mDecor;}
 private void installDecor() {....................此处代码在Activity的视图加载过程已有分析//1.创建DecorView  和window关联  2.拿到ContentParent}
2.2.5.3 继续看createSubDecor()方法,在执行完mWindow.getDecorView()方法后,对subDecor的操作。

通过对窗口特征的判断,使用布局加载器加载相应的xml文件实例化subDecor,这里举例加载文件为R.layout.abc_screen_simple。

final LayoutInflater inflater = LayoutInflater.from(mContext);ViewGroup subDecor = null;if (!mWindowNoTitle) {if (mIsFloating) {// If we're floating, inflate the dialog title decorsubDecor = (ViewGroup) inflater.inflate(R.layout.abc_dialog_title_material, null);// Floating windows can never have an action bar, reset the flagsmHasActionBar = mOverlayActionBar = false;} else if (mHasActionBar) {/*** This needs some explanation. As we can not use the android:theme attribute* pre-L, we emulate it by manually creating a LayoutInflater using a* ContextThemeWrapper pointing to actionBarTheme.*/TypedValue outValue = new TypedValue();mContext.getTheme().resolveAttribute(R.attr.actionBarTheme, outValue, true);Context themedContext;if (outValue.resourceId != 0) {themedContext = new ContextThemeWrapper(mContext, outValue.resourceId);} else {themedContext = mContext;}// Now inflate the view using the themed context and set it as the content viewsubDecor = (ViewGroup) LayoutInflater.from(themedContext).inflate(R.layout.abc_screen_toolbar, null);mDecorContentParent = (DecorContentParent) subDecor.findViewById(R.id.decor_content_parent);mDecorContentParent.setWindowCallback(getWindowCallback());/*** Propagate features to DecorContentParent*/if (mOverlayActionBar) {mDecorContentParent.initFeature(FEATURE_SUPPORT_ACTION_BAR_OVERLAY);}if (mFeatureProgress) {mDecorContentParent.initFeature(Window.FEATURE_PROGRESS);}if (mFeatureIndeterminateProgress) {mDecorContentParent.initFeature(Window.FEATURE_INDETERMINATE_PROGRESS);}}} else {if (mOverlayActionMode) {subDecor = (ViewGroup) inflater.inflate(R.layout.abc_screen_simple_overlay_action_mode, null);} else {subDecor = (ViewGroup) inflater.inflate(R.layout.abc_screen_simple, null);}}if (subDecor == null) {throw new IllegalArgumentException("AppCompat does not support the current theme features: { "+ "windowActionBar: " + mHasActionBar+ ", windowActionBarOverlay: "+ mOverlayActionBar+ ", android:windowIsFloating: " + mIsFloating+ ", windowActionModeOverlay: " + mOverlayActionMode+ ", windowNoTitle: " + mWindowNoTitle+ " }");}if (Build.VERSION.SDK_INT >= 21) {// If we're running on L or above, we can rely on ViewCompat's// setOnApplyWindowInsetsListenerViewCompat.setOnApplyWindowInsetsListener(subDecor, new OnApplyWindowInsetsListener() {@Overridepublic WindowInsetsCompat onApplyWindowInsets(View v,WindowInsetsCompat insets) {final int top = insets.getSystemWindowInsetTop();final int newTop = updateStatusGuard(insets, null);if (top != newTop) {insets = insets.replaceSystemWindowInsets(insets.getSystemWindowInsetLeft(),newTop,insets.getSystemWindowInsetRight(),insets.getSystemWindowInsetBottom());}// Now apply the insets on our viewreturn ViewCompat.onApplyWindowInsets(v, insets);}});} else if (subDecor instanceof FitWindowsViewGroup) {// Else, we need to use our own FitWindowsViewGroup handling((FitWindowsViewGroup) subDecor).setOnFitSystemWindowsListener(new FitWindowsViewGroup.OnFitSystemWindowsListener() {@Overridepublic void onFitSystemWindows(Rect insets) {insets.top = updateStatusGuard(null, insets);}});}if (mDecorContentParent == null) {mTitleView = (TextView) subDecor.findViewById(R.id.title);}// Make the decor optionally fit system windows, like the window's decorViewUtils.makeOptionalFitsSystemWindows(subDecor);
2.2.5.4 ID替换

contentView拿到R.layout.abc_screen_simple的action_bar_activity_content子布局
windowContentView拿到id为content的Framelayout,即当前视图结构的第4个视图
随后将第4个视图的子视图逐一移除并添加到contentView里
windowContentView的id设为NO_ID
contentView的id设为content
随后执行mWindow.setContentView(subDecor);并将subdecor返回。

        final ContentFrameLayout contentView = (ContentFrameLayout) subDecor.findViewById(R.id.action_bar_activity_content);final ViewGroup windowContentView = (ViewGroup) mWindow.findViewById(android.R.id.content);if (windowContentView != null) {// There might be Views already added to the Window's content view so we need to// migrate them to our content viewwhile (windowContentView.getChildCount() > 0) {final View child = windowContentView.getChildAt(0);windowContentView.removeViewAt(0);contentView.addView(child);}// Change our content FrameLayout to use the android.R.id.content id.// Useful for fragments.windowContentView.setId(View.NO_ID);contentView.setId(android.R.id.content);// The decorContent may have a foreground drawable set (windowContentOverlay).// Remove this as we handle it ourselvesif (windowContentView instanceof FrameLayout) {((FrameLayout) windowContentView).setForeground(null);}}// Now set the Window's content view with the decormWindow.setContentView(subDecor);contentView.setAttachListener(new ContentFrameLayout.OnAttachListener() {@Overridepublic void onAttachedFromWindow() {}@Overridepublic void onDetachedFromWindow() {dismissPopups();}});return subDecor;}

在这里插入图片描述
在这里插入图片描述

2.2.5.5 mWindow.setContentView(subDecor)

执行了如下的方法,可以看到此时mContentParent 已经不为空,还是xml文件(这里举例)R.layout.screen_simple里id为Content的子布局,并且将subdecor加载到Content自己的子布局下。
subdecor为第5个视图
改名前id名为action_bar_activity_content的布局为第6个视图

2.2.6 退出createSubDecor(),退出ensureSubDecor(),关于布局加载的代码已经完毕

此时回到了setContentView()方法,
ViewGroup contentParent = mSubDecor.findViewById(android.R.id.content);
此时id为content的布局为R.layout.abc_screen_simple的子布局action_bar_activity_content,当然了,在上一个步骤里,他已经被改名content了。
contentParent.removeAllViews();//移除自身的所有子布局
LayoutInflater.from(mContext).inflate(resId, contentParent); //将自己的R.layout.xxx添加进来
自己的R.layout.xxx为第7个视图

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

三、总结

每个Activity都会获得一个窗口,那就是Window,它用于绘制用户的UI界面
Window是一个抽象类,提供了绘制窗口的一组通用API,PhoneWindow是它的唯一实现类
DecorView是所有应用窗口的根节点。是FrameLayout的子类
PhoneWindow内部实现xml布局文件的加载,容器为根据feature进行加载的对应的layout布局中android:id="@android:id/content"的ViewGroup类型的容器(AppCompatActivity)


本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/828479.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

SD-WAN怎样保障网络稳定

随着企业网络的日益复杂&#xff0c;如何确保线路的稳定性和高效性成为了网络管理的一大挑战。尤其是在线路出现故障、质量下降或拥塞时&#xff0c;如何快速响应并切换到最佳线路&#xff0c;就显得尤为重要。SD-WAN&#xff0c;作为一种新型的网络架构&#xff0c;为用户提供…

周报不止是汇报进度,如何用周报轻松提升团队协作效率?

周报是工作中常见的沟通工具&#xff0c;对于项目经理来说尤其重要。写周报不仅仅是为了完成一项任务&#xff0c;它更是项目管理中不可或缺的环节&#xff0c;它不仅有助于项目经理跟踪项目进度&#xff0c;还加强了团队成员间的沟通与协作。以下是几个关键的原因&#xff1a;…

北京车展打响新汽车“第一枪”,长安造车40年,开启“汽车机器人”时代

4月25日&#xff0c;睽违四年的2024(第十八届)北京国际汽车展览会正式启幕&#xff0c;此次车展以“新时代 新汽车”为主题&#xff0c;吸引全球1500余家主流车企及零部件制造商同台“打擂”。其中&#xff0c;长安汽车以“数智启源随你而变”为主题&#xff0c;携各子品牌及合…

掌握未来通信技术:5G核心网基础入门

&#x1f525;个人主页&#xff1a;Quitecoder &#x1f525;专栏&#xff1a;5GC笔记仓 朋友们大家好&#xff0c;本篇文章是我们新内容的开始&#xff0c;我们本篇进入5GC的学习&#xff0c;希望大家多多支持&#xff01; 目录 一.核心网的演进2G核心网2.5G核心网3G核心网4G…

六个月滴滴实习:轻松、舒心又高薪!

不久前&#xff0c;一位在滴滴后端研发部门实习了六个月的小伙伴在牛客网上分享了他的实习体验&#xff0c; 作者详细描述了他在滴滴的实习生活。 从他的叙述中&#xff0c;我们可以感受到与其他互联网公司相比&#xff0c;滴滴的工作环境显得相对轻松和舒适。 他提到&#x…

ROS摄像机标定

文章目录 一、环境准备二、摄像头标定2.1 为什么要标定2.2 标定前准备2.2.1 标定板2.2.2 摄像头调焦 2.3 开始标定2.4 测试标定结果 总结参考资料 一、环境准备 安装usb_cam相机驱动 sudo apt-get install ros-noetic-usb-cam 安装标定功能包 sudo apt-get install ros-noet…

深度学习——常用激活函数解析与对比

1、 简介 在神经网络中&#xff0c;激活函数扮演着至关重要的角色。它们的主要目的是引入非线性因素&#xff0c;使得网络能够学习和表示更加复杂的函数映射。以下是激活函数应具备的特点&#xff0c;以及这些特点为何重要的详细解释&#xff1a; 引入非线性有助于优化网络&am…

【Ant-Desgin-React 步骤条】步骤条配合组件使用

步骤条配合组件使用 基础使用多分组进度 基础使用 /* eslint-disable no-unused-vars */ import React, { useState } from react import { Button, message, Steps, theme } from antd import After from ./components/after import Now from ./components/now const steps …

Docker 安装 Mongo

创建宿主机目录 在你的宿主机上创建必要的目录来存储 MongoDB 的数据和配置文件。这样做可以保证即使容器被删除&#xff0c;数据也能得到保留。 mkdir -p /develop/mongo/data mkdir -p /develop/mongo/config创建 MongoDB 配置文件 创建一个名为 mongod.conf 的 MongoDB 配…

RestfulApi RestTemplate代码规范介绍

1.介绍 1.1 RestfulApi Restful API 是一种设计风格&#xff0c;代表了使用 HTTP 协议构建 web 服务的一种架构原则。REST&#xff08;Representational State Transfer&#xff09;的核心思想是&#xff0c;通过 URL 定位资源&#xff0c;使用 HTTP 方法&#xff08;GET, POS…

MySQL多版本并发控制mvcc原理浅析

文章目录 1.mvcc简介1.1mvcc定义1.2mvcc解决的问题1.3当前读与快照读 2.mvcc原理2.1隐藏字段2.2版本链2.3ReadView2.4读视图生成原则 3.rc和rr隔离级别下mvcc的不同 1.mvcc简介 1.1mvcc定义 mvcc(Multi Version Concurrency Control)&#xff0c;多版本并发控制&#xff0c;是…

golang学习笔记(defer基础知识)

什么是defer defer语句用于golang程序中延迟函数的调用&#xff0c; 每次defer都会把一个函数压入栈中&#xff0c; 函数返回前再把延迟的函数取出并执行。 为了方便描述&#xff0c; 我们把创建defer的函数称为主函数&#xff0c; defer语句后面的函数称为延迟函数。延迟函数…

npm常用的命令大全(2024-04-21)

nodejs中npm常见的命令 npm主要是node包管理和发布的工具。 npm官网网址&#xff1a;npm | Homehttps://www.npmjs.com/官网英文文档&#xff1a; npm DocsDocumentation for the npm registry, website, and command-line interfacehttps://docs.npmjs.com/about-npm官网中文文…

同城便民信息小程序源码系统:相亲交友+拼车顺风车功能 带完整的安装代码包以及搭建教程

在信息化、数字化的时代&#xff0c;人们的生活越来越离不开各种智能应用。其中&#xff0c;小程序作为一种轻量级、便捷的应用形式&#xff0c;正逐渐渗透到我们日常生活的方方面面。今天&#xff0c;我们要介绍的这款“智慧同城便民信息小程序源码系统”&#xff0c;不仅集成…

每日一题:跳跃游戏II

给定一个长度为 n 的 0 索引整数数组 nums。初始位置为 nums[0]。 每个元素 nums[i] 表示从索引 i 向前跳转的最大长度。换句话说&#xff0c;如果你在 nums[i] 处&#xff0c;你可以跳转到任意 nums[i j] 处: 0 < j < nums[i] i j < n 返回到达 nums[n - 1] 的最…

CAS机制(Compare And Swap)源码解读与三大问题

&#x1f3f7;️个人主页&#xff1a;牵着猫散步的鼠鼠 &#x1f3f7;️系列专栏&#xff1a;Java源码解读-专栏 &#x1f3f7;️个人学习笔记&#xff0c;若有缺误&#xff0c;欢迎评论区指正 目录 1. 前言 2. 原子性问题 3. 乐观锁与悲观锁 4. CAS操作 5. CAS算法带来的…

西米支付:支付行业中,“清算、结算、清结算”之间的区别

做支付最头疼的三个词莫过于“清算、结算、清结算”&#xff0c; 傻傻分不清&#xff0c;偶尔清晰偶尔混沌&#xff0c;有时候吧觉得自己很清晰了&#xff0c;突然跟别人聊天或者看书、看文章时又觉得糊涂起来了&#xff0c;在一些场景里好像很清晰&#xff0c;但是到了另一些…

几种免费SSL证书申请方式

目录 DV单域名免费证书的获取渠道&#xff1a; DV多域名免费证书获取渠道&#xff1a; DV通配符免费证书获取渠道&#xff1a; 随着现在网络安全意识的逐渐提升&#xff0c;越来越多的网站都在相继配对部署SSL证书&#xff0c;用以实现https访问。 大家都知道SSL证书好&…

数据分析学习资源(未完)

1、PDF 数据分析自学攻略 增长黑客&#xff08;AARRR&#xff09; 量化思维

C++ | Leetcode C++题解之第47题全排列II

题目&#xff1a; 题解&#xff1a; class Solution { public:vector<vector<int>> permuteUnique(vector<int>& nums) {dfs(nums, 0);return res;} private:vector<vector<int>> res;void dfs(vector<int> nums, int x) {if (x num…