对于Android的开发者来说,setContentView大家再熟悉不过了,在我们的Activity中首先就是要用它加载我们的布局,但是应该有一部分人是不知道加载布局的原理,也包括我,今天就从源码的角度分析setContentView加载布局原理。
在图中我们看到第8步:ActivityThread 调用 handleLaunchActivity() 方法,执行 Activity 的oncreate() 方法,而我们的 xml布局文件就是在oncreate()中通过:setContentView(R.layout.activity_main); 来加载的。下面就去看一下其内部如何实现的。
(看源码的时候,我们习惯倒着或向上或向内部一步步去追踪代码,这里我们反着以正向来分析)
首先看 ActivityThread 的 handleLaunchActivity() 方法:
/*** Extended implementation of activity launch. Used when server requests a launch or relaunch.*/@Overridepublic Activity handleLaunchActivity(ActivityClientRecord r,PendingTransactionActions pendingActions, 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 activityif (!ThreadedRenderer.sRendererDisabled) {GraphicsEnvironment.earlyInitEGL();}WindowManagerGlobal.initialize();
final Activity a = performLaunchActivity(r, customIntent);
if (a != null) {r.createdConfig = new Configuration(mConfiguration);reportSizeConfigurations(r);if (!r.activity.mFinished && pendingActions != null) {pendingActions.setOldState(r.state);pendingActions.setRestoreInstanceState(true);pendingActions.setCallOnPostCreate(true);}} else {// If there was an error, for any reason, tell the activity manager to stop us.try {ActivityManager.getService().finishActivity(r.token, Activity.RESULT_CANCELED, null,Activity.DONT_FINISH_TASK_WITH_ACTIVITY);} catch (RemoteException ex) {throw ex.rethrowFromSystemServer();}}
return a;}
代码简单来说,就是通过 performLaunchActivity()创建了一个activity,然后返回。去看看他如何创建activity:
/** Core implementation of activity launch. */private Activity performLaunchActivity(ActivityClientRecord r, Intent customIntent) {ActivityInfo aInfo = r.activityInfo;if (r.packageInfo == null) {r.packageInfo = getPackageInfo(aInfo.applicationInfo, r.compatInfo,Context.CONTEXT_INCLUDE_CODE);}
ComponentName component = r.intent.getComponent();if (component == null) {component = r.intent.resolveActivity(mInitialApplication.getPackageManager());r.intent.setComponent(component);}
if (r.activityInfo.targetActivity != null) {component = new ComponentName(r.activityInfo.packageName,r.activityInfo.targetActivity);}
ContextImpl appContext = createBaseContextForActivity(r);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);}}
try {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) {CharSequence title = r.activityInfo.loadLabel(appContext.getPackageManager());Configuration config = new Configuration(mCompatConfiguration);if (r.overrideConfig != null) {config.updateFrom(r.overrideConfig);}if (DEBUG_CONFIGURATION) Slog.v(TAG, "Launching activity "+ r.activityInfo.name + " with config " + config);Window window = null;if (r.mPendingRemoveWindow != null && r.mPreserveWindow) {window = r.mPendingRemoveWindow;r.mPendingRemoveWindow = null;r.mPendingRemoveWindowManager = null;}appContext.setOuterContext(activity);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);
if (customIntent != null) {activity.mIntent = customIntent;}r.lastNonConfigurationInstances = null;checkAndBlockForNetworkAccess();activity.mStartedActivity = false;int theme = r.activityInfo.getThemeResource();if (theme != 0) {activity.setTheme(theme);}
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.setState(ON_CREATE);
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;}
总的来说就是通过反射创建了activity,然后执行了activity的attach方法(在attach方法中初始化了 PhoneWindow ),再然后回调activity的onCreate方法,在oncreate方法中再通过PhoneWindow 的 setContentView 方法加载布局。
内部初始化了 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*/);
// 初始化 mWindow mWindow = new PhoneWindow(this, window, activityConfigCallback);mWindow.setWindowControllerCallback(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;
mWindow.setColorMode(info.colorMode);
setAutofillCompatibilityEnabled(application.isAutofillCompatibilityEnabled());enableAutofillCompatibilityIfNeeded();}
performLaunchActivity()中回调activity的onCreate方法
if (r.isPersistable()) {mInstrumentation.callActivityOnCreate(activity, r.state, r.persistentState);
} else {mInstrumentation.callActivityOnCreate(activity, r.state);
}
mInstrumentation的callActivityOnCreate方法(关于mInstrumentation单独去学习):
/*** Perform calling of an activity's {@link Activity#onCreate}* method. The default implementation simply calls through to that method.* @param activity The activity being created.* @param icicle The previously frozen state (or null) to pass through to* @param persistentState The previously persisted state (or null)*/public void callActivityOnCreate(Activity activity, Bundle icicle,PersistableBundle persistentState) {prePerformCreate(activity);activity.performCreate(icicle, persistentState);postPerformCreate(activity);}
callActivityOnCreate 中的 performCreate 方法:
final void performCreate(Bundle icicle) {performCreate(icicle, null);}
final void performCreate(Bundle icicle, PersistableBundle persistentState) {mCanEnterPictureInPicture = true;restoreHasCurrentPermissionRequest(icicle);if (persistentState != null) {onCreate(icicle, persistentState);} else {onCreate(icicle);}writeEventLog(LOG_AM_ON_CREATE_CALLED, "performCreate");mActivityTransitionState.readState(icicle);
mVisibleFromClient = !mWindow.getWindowStyle().getBoolean(com.android.internal.R.styleable.Window_windowNoDisplay, false);mFragments.dispatchActivityCreated();mActivityTransitionState.setEnterActivityOptions(this, getActivityOptions());}
然后在activity的oncreate方法中去调用 setContentView方法,这是 onCreate 的注释:
Called when the activity is starting. This is where most initialization should go: calling {@link #setContentView(int)} to inflate the activity’s UI, using {@link #findViewById} to programmatically interact with widgets in the UI, calling {@link #managedQuery(android.net.Uri , String[], String, String[], String)} to retrievecursors for data being displayed, etc.
/*** Set the activity content from a layout resource. The resource will be* inflated, adding all top-level views to the activity.** @param layoutResID Resource ID to be inflated.** @see #setContentView(android.view.View)* @see #setContentView(android.view.View, android.view.ViewGroup.LayoutParams)*/public void setContentView(@LayoutRes int layoutResID) {getWindow().setContentView(layoutResID);initWindowDecorActionBar();}
/*** Set the activity content to an explicit view. This view is placed* directly into the activity's view hierarchy. It can itself be a complex* view hierarchy. When calling this method, the layout parameters of the* specified view are ignored. Both the width and the height of the view are* set by default to {@link ViewGroup.LayoutParams#MATCH_PARENT}. To use* your own layout parameters, invoke* {@link #setContentView(android.view.View, android.view.ViewGroup.LayoutParams)}* instead.** @param view The desired content to display.** @see #setContentView(int)* @see #setContentView(android.view.View, android.view.ViewGroup.LayoutParams)*/public void setContentView(View view) {getWindow().setContentView(view);initWindowDecorActionBar();}
/*** Set the activity content to an explicit view. This view is placed* directly into the activity's view hierarchy. It can itself be a complex* view hierarchy.** @param view The desired content to display.* @param params Layout parameters for the view.** @see #setContentView(android.view.View)* @see #setContentView(int)*/public void setContentView(View view, ViewGroup.LayoutParams params) {getWindow().setContentView(view, params);initWindowDecorActionBar();}
这个 getWindow() 就是在attach方法中初始化的PhoneWindow对象,那去PhoneWindow看下他的 setContentView 方法:
@Overridepublic 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. 创建 DecorView,拿到 mContentParent: installDecor();
\2. 将我们的界面xml渲染到mContentParent :
mLayoutInflater.inflate(layoutResID, mContentParent);
3.将mContentParentExplicitlySet 置为 true(这个重要,下面会有解释)
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.makeOptionalFitsSystemWindows();
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 && mBackgroundFallbackResource != 0) {mDecor.setBackgroundFallback(mBackgroundFallbackResource);}
// 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);}}}}
里面重点看两个方法:
mDecor = generateDecor(-1); 和 mContentParent = generateLayout(mDecor);
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, getContext());if (mTheme != -1) {context.setTheme(mTheme);}}} else {context = getContext();}return new DecorView(context, featureId, this, getAttributes());}
创建了DecorView,然后返回。
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);}
mIsFloating = a.getBoolean(R.styleable.Window_windowIsFloating, false);int flagsToUpdate = (FLAG_LAYOUT_IN_SCREEN|FLAG_LAYOUT_INSET_DECOR)& (~getForcedWindowFlags());if (mIsFloating) {setLayout(WRAP_CONTENT, WRAP_CONTENT);setFlags(0, flagsToUpdate);} else {setFlags(FLAG_LAYOUT_IN_SCREEN|FLAG_LAYOUT_INSET_DECOR, flagsToUpdate);}
if (a.getBoolean(R.styleable.Window_windowNoTitle, false)) {requestFeature(FEATURE_NO_TITLE);} else if (a.getBoolean(R.styleable.Window_windowActionBar, false)) {// Don't allow an action bar if there is no title.requestFeature(FEATURE_ACTION_BAR);}
if (a.getBoolean(R.styleable.Window_windowActionBarOverlay, false)) {requestFeature(FEATURE_ACTION_BAR_OVERLAY);}
if (a.getBoolean(R.styleable.Window_windowActionModeOverlay, false)) {requestFeature(FEATURE_ACTION_MODE_OVERLAY);}
if (a.getBoolean(R.styleable.Window_windowSwipeToDismiss, false)) {requestFeature(FEATURE_SWIPE_TO_DISMISS);}
if (a.getBoolean(R.styleable.Window_windowFullscreen, false)) {setFlags(FLAG_FULLSCREEN, FLAG_FULLSCREEN & (~getForcedWindowFlags()));}
if (a.getBoolean(R.styleable.Window_windowTranslucentStatus,false)) {setFlags(FLAG_TRANSLUCENT_STATUS, FLAG_TRANSLUCENT_STATUS& (~getForcedWindowFlags()));}
if (a.getBoolean(R.styleable.Window_windowTranslucentNavigation,false)) {setFlags(FLAG_TRANSLUCENT_NAVIGATION, FLAG_TRANSLUCENT_NAVIGATION& (~getForcedWindowFlags()));}
if (a.getBoolean(R.styleable.Window_windowOverscan, false)) {setFlags(FLAG_LAYOUT_IN_OVERSCAN, FLAG_LAYOUT_IN_OVERSCAN&(~getForcedWindowFlags()));}
if (a.getBoolean(R.styleable.Window_windowShowWallpaper, false)) {setFlags(FLAG_SHOW_WALLPAPER, FLAG_SHOW_WALLPAPER&(~getForcedWindowFlags()));}
if (a.getBoolean(R.styleable.Window_windowEnableSplitTouch,getContext().getApplicationInfo().targetSdkVersion>= android.os.Build.VERSION_CODES.HONEYCOMB)) {setFlags(FLAG_SPLIT_TOUCH, FLAG_SPLIT_TOUCH&(~getForcedWindowFlags()));}
a.getValue(R.styleable.Window_windowMinWidthMajor, mMinWidthMajor);a.getValue(R.styleable.Window_windowMinWidthMinor, mMinWidthMinor);if (DEBUG) Log.d(TAG, "Min width minor: " + mMinWidthMinor.coerceToString()+ ", major: " + mMinWidthMajor.coerceToString());if (a.hasValue(R.styleable.Window_windowFixedWidthMajor)) {if (mFixedWidthMajor == null) mFixedWidthMajor = new TypedValue();a.getValue(R.styleable.Window_windowFixedWidthMajor,mFixedWidthMajor);}if (a.hasValue(R.styleable.Window_windowFixedWidthMinor)) {if (mFixedWidthMinor == null) mFixedWidthMinor = new TypedValue();a.getValue(R.styleable.Window_windowFixedWidthMinor,mFixedWidthMinor);}if (a.hasValue(R.styleable.Window_windowFixedHeightMajor)) {if (mFixedHeightMajor == null) mFixedHeightMajor = new TypedValue();a.getValue(R.styleable.Window_windowFixedHeightMajor,mFixedHeightMajor);}if (a.hasValue(R.styleable.Window_windowFixedHeightMinor)) {if (mFixedHeightMinor == null) mFixedHeightMinor = new TypedValue();a.getValue(R.styleable.Window_windowFixedHeightMinor,mFixedHeightMinor);}if (a.getBoolean(R.styleable.Window_windowContentTransitions, false)) {requestFeature(FEATURE_CONTENT_TRANSITIONS);}if (a.getBoolean(R.styleable.Window_windowActivityTransitions, false)) {requestFeature(FEATURE_ACTIVITY_TRANSITIONS);}
mIsTranslucent = a.getBoolean(R.styleable.Window_windowIsTranslucent, false);
final Context context = getContext();final int targetSdk = context.getApplicationInfo().targetSdkVersion;final boolean targetPreHoneycomb = targetSdk < android.os.Build.VERSION_CODES.HONEYCOMB;final boolean targetPreIcs = targetSdk < android.os.Build.VERSION_CODES.ICE_CREAM_SANDWICH;final boolean targetPreL = targetSdk < android.os.Build.VERSION_CODES.LOLLIPOP;final boolean targetHcNeedsOptions = context.getResources().getBoolean(R.bool.target_honeycomb_needs_options_menu);final boolean noActionBar = !hasFeature(FEATURE_ACTION_BAR) || hasFeature(FEATURE_NO_TITLE);
if (targetPreHoneycomb || (targetPreIcs && targetHcNeedsOptions && noActionBar)) {setNeedsMenuKey(WindowManager.LayoutParams.NEEDS_MENU_SET_TRUE);} else {setNeedsMenuKey(WindowManager.LayoutParams.NEEDS_MENU_SET_FALSE);}
if (!mForcedStatusBarColor) {mStatusBarColor = a.getColor(R.styleable.Window_statusBarColor, 0xFF000000);}if (!mForcedNavigationBarColor) {mNavigationBarColor = a.getColor(R.styleable.Window_navigationBarColor, 0xFF000000);mNavigationBarDividerColor = a.getColor(R.styleable.Window_navigationBarDividerColor,0x00000000);}
WindowManager.LayoutParams params = getAttributes();
// Non-floating windows on high end devices must put up decor beneath the system bars and// therefore must know about visibility changes of those.if (!mIsFloating) {if (!targetPreL && a.getBoolean(R.styleable.Window_windowDrawsSystemBarBackgrounds,false)) {setFlags(FLAG_DRAWS_SYSTEM_BAR_BACKGROUNDS,FLAG_DRAWS_SYSTEM_BAR_BACKGROUNDS & ~getForcedWindowFlags());}if (mDecor.mForceWindowDrawsStatusBarBackground) {params.privateFlags |= PRIVATE_FLAG_FORCE_DRAW_STATUS_BAR_BACKGROUND;}}if (a.getBoolean(R.styleable.Window_windowLightStatusBar, false)) {decor.setSystemUiVisibility(decor.getSystemUiVisibility() | View.SYSTEM_UI_FLAG_LIGHT_STATUS_BAR);}if (a.getBoolean(R.styleable.Window_windowLightNavigationBar, false)) {decor.setSystemUiVisibility(decor.getSystemUiVisibility() | View.SYSTEM_UI_FLAG_LIGHT_NAVIGATION_BAR);}if (a.hasValue(R.styleable.Window_windowLayoutInDisplayCutoutMode)) {int mode = a.getInt(R.styleable.Window_windowLayoutInDisplayCutoutMode, -1);if (mode < LAYOUT_IN_DISPLAY_CUTOUT_MODE_DEFAULT|| mode > LAYOUT_IN_DISPLAY_CUTOUT_MODE_NEVER) {throw new UnsupportedOperationException("Unknown windowLayoutInDisplayCutoutMode: "+ a.getString(R.styleable.Window_windowLayoutInDisplayCutoutMode));}params.layoutInDisplayCutoutMode = mode;}
if (mAlwaysReadCloseOnTouchAttr || getContext().getApplicationInfo().targetSdkVersion>= android.os.Build.VERSION_CODES.HONEYCOMB) {if (a.getBoolean(R.styleable.Window_windowCloseOnTouchOutside,false)) {setCloseOnTouchOutsideIfNotSet(true);}}
if (!hasSoftInputMode()) {params.softInputMode = a.getInt(R.styleable.Window_windowSoftInputMode,params.softInputMode);}
if (a.getBoolean(R.styleable.Window_backgroundDimEnabled,mIsFloating)) {/* All dialogs should have the window dimmed */if ((getForcedWindowFlags()&WindowManager.LayoutParams.FLAG_DIM_BEHIND) == 0) {params.flags |= WindowManager.LayoutParams.FLAG_DIM_BEHIND;}if (!haveDimAmount()) {params.dimAmount = a.getFloat(android.R.styleable.Window_backgroundDimAmount, 0.5f);}}
if (params.windowAnimations == 0) {params.windowAnimations = a.getResourceId(R.styleable.Window_windowAnimationStyle, 0);}
// The rest are only done if this window is not embedded; otherwise,// the values are inherited from our container.if (getContainer() == null) {if (mBackgroundDrawable == null) {if (mBackgroundResource == 0) {mBackgroundResource = a.getResourceId(R.styleable.Window_windowBackground, 0);}if (mFrameResource == 0) {mFrameResource = a.getResourceId(R.styleable.Window_windowFrame, 0);}mBackgroundFallbackResource = a.getResourceId(R.styleable.Window_windowBackgroundFallback, 0);if (false) {System.out.println("Background: "+ Integer.toHexString(mBackgroundResource) + " Frame: "+ Integer.toHexString(mFrameResource));}}if (mLoadElevation) {mElevation = a.getDimension(R.styleable.Window_windowElevation, 0);}mClipToOutline = a.getBoolean(R.styleable.Window_windowClipToOutline, false);mTextColor = a.getColor(R.styleable.Window_textColor, Color.TRANSPARENT);}
// 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;setCloseOnSwipeEnabled(true);} 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 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);}}
if ((features & (1 << FEATURE_SWIPE_TO_DISMISS)) != 0) {registerSwipeCallbacks(contentParent);}
// 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;}
代码长了点,实际也简单,
\1. 和我们自定义view有些相似,通过获取自定义属性来设置 mDecor 的状态。 像我们常设置的 “FEATURE_NO_TITLE” 就在这里,这也是我们要把这个属性设置在setContentView之前的原因: setContentView 在加载布局时会通过 requestFeature() 加载我们设置的属性。 \2. 把我们的xml布局文件添加到 decorView (decorView本质是一个FrameLayout): mDecor.onResourcesLoaded(mLayoutInflater, layoutResource);
mLayoutInflater.inflate(layoutResID, mContentParent);
去看这个inflate方法:
public View inflate(@LayoutRes int resource, @Nullable ViewGroup root) {return inflate(resource, root, root != null);}
继续看:
public View inflate(@LayoutRes int resource, @Nullable ViewGroup root, boolean attachToRoot) {final Resources res = getContext().getResources();if (DEBUG) {Log.d(TAG, "INFLATING from resource: "" + res.getResourceName(resource) + "" ("+ Integer.toHexString(resource) + ")");}
final XmlResourceParser parser = res.getLayout(resource);try {return inflate(parser, root, attachToRoot);} finally {parser.close();}}
这里看到时xml解析器:XmlResourceParser ,再进去 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(" 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 xmlfinal 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 suppliedparams = 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) {final InflateException ie = new InflateException(e.getMessage(), e);ie.setStackTrace(EMPTY_STACK_TRACE);throw ie;} catch (Exception e) {final InflateException ie = new InflateException(parser.getPositionDescription()+ ": " + e.getMessage(), e);ie.setStackTrace(EMPTY_STACK_TRACE);throw ie;} finally {// Don't retain static reference on context.mConstructorArgs[0] = lastContext;mConstructorArgs[1] = null;
Trace.traceEnd(Trace.TRACE_TAG_VIEW);}
return result;}}
其中重点看下:
// Temp is the root view that was found in the xml
final View temp = createViewFromTag(root, name, inflaterContext, attrs);
这个就是创建view(这里的view是指rootView)的核心代码:
View createViewFromTag(View parent, String name, Context context, AttributeSet attrs,boolean ignoreThemeAttr) {if (name.equals("view")) {name = attrs.getAttributeValue(null, "class");}
// Apply a theme wrapper, if allowed and one is specified.if (!ignoreThemeAttr) {final TypedArray ta = context.obtainStyledAttributes(attrs, ATTRS_THEME);final int themeResId = ta.getResourceId(0, 0);if (themeResId != 0) {context = new ContextThemeWrapper(context, themeResId);}ta.recycle();}
if (name.equals(TAG_1995)) {// Let's party like it's 1995!return new BlinkLayout(context, attrs);}
try {View view;if (mFactory2 != null) {view = mFactory2.onCreateView(parent, name, context, attrs);} else if (mFactory != null) {view = mFactory.onCreateView(name, context, attrs);} else {view = null;}
if (view == null && mPrivateFactory != null) {view = mPrivateFactory.onCreateView(parent, name, context, attrs);}
if (view == null) {final Object lastContext = mConstructorArgs[0];mConstructorArgs[0] = context;try {if (-1 == name.indexOf('.')) {view = onCreateView(parent, name, attrs);} else {view = createView(name, null, attrs);}} finally {mConstructorArgs[0] = lastContext;}}
return view;} catch (InflateException e) {throw e;
} catch (ClassNotFoundException e) {final InflateException ie = new InflateException(attrs.getPositionDescription()+ ": Error inflating class " + name, e);ie.setStackTrace(EMPTY_STACK_TRACE);throw ie;
} catch (Exception e) {final InflateException ie = new InflateException(attrs.getPositionDescription()+ ": Error inflating class " + name, e);ie.setStackTrace(EMPTY_STACK_TRACE);throw ie;}}
其中主要的:
if (-1 == name.indexOf('.')) {view = onCreateView(parent, name, attrs);
} else {view = createView(name, null, attrs);
}
之所以判断是否有“.”,是用来区分是否是sdk的view:如果带点的话,就是sddk的view;否则为自定义的view(这里的自定义view包含support包里view,比如:android.support.v7.widget.AppCompatTextView)。
下面分别看下 onCreateView 和createView:
protected View onCreateView(View parent, String name, AttributeSet attrs)throws ClassNotFoundException {return onCreateView(name, attrs);}
继续进去:
protected View onCreateView(String name, AttributeSet attrs)throws ClassNotFoundException {return createView(name, "android.view.", attrs);}
这里就错了,他不是直接进入这个 onCreateView 方法,由于 LayoutInflater是一个抽象类,
public abstract class LayoutInflater
所以这里进入的是他的实现类 { PhoneLayoutInflater } 的两个参数的方法中:
/** Override onCreateView to instantiate names that correspond to thewidgets known to the Widget factory. If we don't find a match,call through to our super class.*/@Override protected View onCreateView(String name, AttributeSet attrs) throws ClassNotFoundException {for (String prefix : sClassPrefixList) {try {View view = createView(name, prefix, attrs);if (view != null) {return view;}} catch (ClassNotFoundException e) {// In this case we want to let the base class take a crack// at it.}}
return super.onCreateView(name, attrs);}
这里的 sClassPrefixList 实际上是view的全类名:
private static final String[] sClassPrefixList = {"android.widget.","android.webkit.","android.app."};
// 另外还应该加一个:android.view
实际他最终还是调用了createView,进去:
public final View createView(String name, String prefix, AttributeSet attrs)throws ClassNotFoundException, InflateException {Constructor extends View> constructor = sConstructorMap.get(name);if (constructor != null && !verifyClassLoader(constructor)) {constructor = null;sConstructorMap.remove(name);}Class extends View> clazz = null;
try {Trace.traceBegin(Trace.TRACE_TAG_VIEW, name);
if (constructor == null) {// Class not found in the cache, see if it's real, and try to add itclazz = mContext.getClassLoader().loadClass(prefix != null ? (prefix + name) : name).asSubclass(View.class);
if (mFilter != null && clazz != null) {boolean allowed = mFilter.onLoadClass(clazz);if (!allowed) {failNotAllowed(name, prefix, attrs);}}constructor = clazz.getConstructor(mConstructorSignature);constructor.setAccessible(true);sConstructorMap.put(name, constructor);} else {// If we have a filter, apply it to cached constructorif (mFilter != null) {// Have we seen this name before?Boolean allowedState = mFilterMap.get(name);if (allowedState == null) {// New class -- remember whether it is allowedclazz = mContext.getClassLoader().loadClass(prefix != null ? (prefix + name) : name).asSubclass(View.class);
boolean allowed = clazz != null && mFilter.onLoadClass(clazz);mFilterMap.put(name, allowed);if (!allowed) {failNotAllowed(name, prefix, attrs);}} else if (allowedState.equals(Boolean.FALSE)) {failNotAllowed(name, prefix, attrs);}}}
Object lastContext = mConstructorArgs[0];if (mConstructorArgs[0] == null) {// Fill in the context if not already within inflation.mConstructorArgs[0] = mContext;}Object[] args = mConstructorArgs;args[1] = attrs;
final View view = constructor.newInstance(args);if (view instanceof ViewStub) {// Use the same context when inflating ViewStub later.final ViewStub viewStub = (ViewStub) view;viewStub.setLayoutInflater(cloneInContext((Context) args[0]));}mConstructorArgs[0] = lastContext;return view;
} catch (NoSuchMethodException e) {final InflateException ie = new InflateException(attrs.getPositionDescription()+ ": Error inflating class " + (prefix != null ? (prefix + name) : name), e);ie.setStackTrace(EMPTY_STACK_TRACE);throw ie;
} catch (ClassCastException e) {// If loaded class is not a View subclassfinal InflateException ie = new InflateException(attrs.getPositionDescription()+ ": Class is not a View " + (prefix != null ? (prefix + name) : name), e);ie.setStackTrace(EMPTY_STACK_TRACE);throw ie;} catch (ClassNotFoundException e) {// If loadClass fails, we should propagate the exception.throw e;} catch (Exception e) {final InflateException ie = new InflateException(attrs.getPositionDescription() + ": Error inflating class "+ (clazz == null ? "" : clazz.getName()), e);ie.setStackTrace(EMPTY_STACK_TRACE);throw ie;} finally {Trace.traceEnd(Trace.TRACE_TAG_VIEW);}}
内部实际上就是通过反射创建对应的view实例,然后返回。
注意这个标记:mContentParentExplicitlySet 是“不能设置decorView属性”的一个标记,在 setContentView 完成后会把这个变量标记为true,之后再通过 requestFeature() 设置 window的属性会报错:requestFeature() must be called before adding content 。 如下:
if (mContentParentExplicitlySet) {throw new AndroidRuntimeException("requestFeature() must be called before adding content");}
他内部对标记:mContentParentExplicitlySet 做了一个判断。
所以我们平时想要隐藏标题栏需要将:
getWindow().requestFeature(Window.FEATURE_NO_TITLE);
设置到 setContentView()之前的原因。

以上就是这篇文章的全部内容了,到这里setContentView已经分析完毕,希望本文的内容对大家的学习或者工作具有一定的参考学习价值,由于水平有限,难免有错误。更多framework进阶学习,前往《framework源码解读》
1、setContentView的作用是将View加载到根view之上,这样当显示view时,先显示根view,然后在显示子view,以此类推,最终将所有view显示出来。
2、setContentView必须要放在findviewbyid之前,因为view在加载之前是无法引用的。
3、setContentView最本质的作用是为要显示的view分配内存。
4、activity、window和view之间的关系:
而当我们运行程序的时候,有一个setContentView()方法,Activity其实不是显示视图(直观上感觉是它),实际上Activity调用了PhoneWindow的setContentView()方法,然后加载视图,将视图放到这个Window上,而Activity其实构造的时候初始化的是Window(PhoneWindow),Activity其实是个控制单元,即可视的人机交互界面。
上一篇:关于如果的句子伤感
下一篇:激励人的正能量的句子