一、生命周期五种状态
(1)启动状态:Activity的启动状态很短暂,当Activity启动后便会进入运行状态。
(2)运行状态:Activity在此状态时处于屏幕最前端,它是可见、有焦点的,可以与用户进行交互。如单击、长按等事件。即使出现内存不足的情况,Android也会先销毁栈底的Activity,来确保当前的Activity正常运行。
(3)暂停状态:在某些情况下,Activity对用户来说仍然可见,但它无法获取焦点,用户对它操作没有没有响应,此时它处于暂停状态。
(4)停止状态:当Activity完全不可见时,它处于停止状态,但仍然保留着当前的状态和成员信息。如系统内存不足,那么这种状态下的Activity很容易被销毁。
(5)销毁状态:当Activity处于销毁状态时,将被清理出内存。
二、生命周期七种方法
(1)onCreate()方法:在Activity创建时调用,通常做一些初始化设置。
(2)onStart()方法:在Activity即将可见时调用。
(3)onResume()方法:在Activity获取焦点开始与用户交互时调用。
(4)onPause()方法:在当前Activity被其他Activity覆盖或锁屏时调用。
(5)onStop()方法:在Activity对用户不可见时调用。
(6)onDestroy()方法:在Activity销毁时调用。
(7)onRestart()方法:在Activity从停止状态再次启动时调用
三、onCreate和onStart之间有什么区别?
(1)可见与不可见的区别。前者不可见,后者可见。
(2)onCreate方法只在Activity创建时执行一次,而onStart方法在Activity中被多次调用。onCreate能做的事onStart其实都能做,但是onstart能做的事onCreate却未必适合做。如前文所说的,setContentView和资源初始化在两者都能做,然而想动画的初始化在onStart中做比较好。
四、onStart方法和onResume方法有什么区别?
(1)是否在前台。onStart方法中Activity可见但不在前台,不可交互,而在onResume中在前台。
(2)职责不同,onStart方法中主要还是进行初始化工作,而onResume方法,根据官方的建议,可以做开启动画和独占设备的操作。
五、onPause方法和onStop方法有什么区别?
(1)是否可见。onPause时Activity可见,onStop时Activity不可见,但Activity对象还在内存中。
(2)在系统内存不足的时候可能不会执行onStop方法,因此程序状态的保存、独占设备和动画的关闭、以及一些数据的保存最好在onPause中进行,但要注意不能太耗时。
六、切换Activity的生命周期和横竖屏的生命周期
(1)切换Activity时各方法的执行次序是
(A)onPause→(B)onCreate→(B)onStart→(B)onResume→(A)onStop 。(onPause方法将会释放掉很多系统资源,为切换Activity提供流畅性的保障,而不需要再等多两个阶段,这样做切换更快。)
(2)当手机横竖屏切换时,会根据AndroidManifest.xml文件中Activity的configChanges属性不同而调用不同的生命周期方法。当使用默认属性时,Activity生命周期会依次调用onCreate()、onStart()、onResume()方法,当进行横竖屏切换时,调用方法依次是onPause()、onStop()、onDestroy()、onCreate()、onStart()、onResume()方法。
七、 Activity的四种启动模式:standard、singleTop、singleTask、singleInstance
(1)standard模式:Standard模式是Android的默认启动模式,你不在配置文件中做任何设置,那么这个Activity就是standard模式,这种模式下,Activity可以有多个实例,每次启动Activity,无论任务栈中是否已经有这个Activity的实例,系统都会创建一个新的Activity实例
(2)singleTop模式:SingleTop模式和standard模式非常相似,主要区别就是当一个singleTop模式的Activity已经位于任务栈的栈顶,再去启动它时,不会再创建新的实例,如果不位于栈顶,就会创建新的实例。
(3)singleTask模式:SingleTask模式的Activity在同一个Task内只有一个实例,如果Activity已经位于栈顶,系统不会创建新的Activity实例,和singleTop模式一样。但Activity已经存在但不位于栈顶时,系统就会把该Activity移到栈顶,并把它上面的activity出栈。
(4)singleInstance模式:singleInstance模式也是单例的,但和singleTask不同,singleTask只是任务栈内单例,系统里是可以有多个singleTask Activity实例的,而singleInstance Activity在整个系统里只有一个实例,启动一singleInstanceActivity时,系统会创建一个新的任务栈,并且这个任务栈只有他一个Activity。
2.setContentView 方法
它的作用就是把我们的布局文件放在Activity中显示
setContentView(R.layout.activity_main);
调用Activity的setContentView 方法
public void setContentView(@LayoutRes int layoutResID) { getWindow().setContentView(layoutResID); initWindowDecorActionBar(); }
这个getWindow是获取当前Activity的Window,在Android中Window的实现类是PhoneWindow,所以我们要看PhoneWindow的setContentView
顺便提一下Activity的window的创建时机是在Activity的attach方法:
frameworks\base\core\java\com\android\internal\policy\PhoneWindow.java
phoneWindow的setContentView
重点关注installDecor和mLayoutInflater.inflate方法
@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) {mContentParent 是 ViewGroup类型的 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; }
installDecor 重点关注generateDecor(-1)和generateLayout(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); } } } }
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()); }
创建了一个DecorView并且返回之后赋值给了mDecor
frameworks\base\core\java\com\android\internal\policy\DecorView.java
public class DecorView extends FrameLayout implements RootViewSurfaceTaker, WindowCallbacks
generateLayout(mDecor)
protected ViewGroup generateLayout(DecorView decor) { 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; }
这个方法的作用就是,通过我们设置的style或者requestWindowFuture等来选出一个系统自带的布局文件,默认的是R.layout.screen_simple,选出布局文件后,通过调用mDecor.onResourcesLoaded(mLayoutInflater, layoutResource);方法inflate出来后,add到DecorView上,我们详细看一下R.layout.screen_simple这个布局文件:
frameworks/base/core/res/res/layout/screen_simple.xml
<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>
frameworks\base\core\java\com\android\internal\policy\DecorView.java
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(); }
这个布局文件,被inflate到DecorView上,然后通过
ViewGroup contentParent = (ViewGroup)findViewById(ID_ANDROID_CONTENT);
这一句获取到这个是content的FrameLayout,然后返回了这个contentParent
到这里installDecor这个方法分析完了
总结一下:installDecor方法主要就是创建DecorView,然后把选出的布局文件add到DecorView上,然后再通过findViewbyId找到类型是FrameLayout的content赋值给contentParent 返回,其实还有一步是DecorView和phoneWindow结合,这里不细说,以后有FrameWorker源码解析再说。
接下来分析:mLayoutInflater.inflate(layoutResID, mContentParent);
这句很明显,layoutResID是我们的activity_main.layout这种自己写的布局文件,把它inflate到mContentParent中,通过图片让大家有一个更清晰的感官:
继续分析phoneWindow的setContentView的第三个关键流程重点
mContentParentExplicitlySet = true;
这个flag的作用首先我们先看一段代码:
public class MainActivity extends AppCompatActivity { @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); requestWindowFeature(Window.FEATURE_NO_TITLE); } }
这段代码运行会报错:
requestFeature() must be called before adding content
为什么会报这个错误呢,从代码上来找:
public final boolean requestWindowFeature(int featureId) { return getWindow().requestFeature(featureId); } //我们已经知道,getWindow其实获取的是PhoneWindow所以调用的是PhoneWindow的requestFeature @Override public boolean requestFeature(int featureId) { if (mContentParentExplicitlySet) { //⭐这就是报错的根源 throw new AndroidRuntimeException("requestFeature() must be called before adding content"); } final int features = getFeatures(); final int newFeatures = features | (1 << featureId); if ((newFeatures & (1 << FEATURE_CUSTOM_TITLE)) != 0 && (newFeatures & ~CUSTOM_TITLE_COMPATIBLE_FEATURES) != 0) { // Another feature is enabled and the user is trying to enable the custom title feature // or custom title feature is enabled and the user is trying to enable another feature throw new AndroidRuntimeException( "You cannot combine custom titles with other title features"); } if ((features & (1 << FEATURE_NO_TITLE)) != 0 && featureId == FEATURE_ACTION_BAR) { return false; // Ignore. No title dominates. } if ((features & (1 << FEATURE_ACTION_BAR)) != 0 && featureId == FEATURE_NO_TITLE) { // Remove the action bar feature if we have no title. No title dominates. removeFeature(FEATURE_ACTION_BAR); } if (featureId == FEATURE_INDETERMINATE_PROGRESS && getContext().getPackageManager().hasSystemFeature(PackageManager.FEATURE_WATCH)) { throw new AndroidRuntimeException("You cannot use indeterminate progress on a watch."); } return super.requestFeature(featureId); }
看到了报错的根源,其实就是mContentParentExplicitlySet这个flag,在setContentView执行完就设置成了true,所以调用requestWindowFeature(Window.FEATURE_NO_TITLE);方法必须在setContentView之前,否则就会抛出异常,最后从设计的角度分析,为什么要设计这么一个flag呢,或者说为什么非要在setContentView之前执行requestFeature,因为在setContentView中需要通过设置的这些requestWindowFeature的flag去选择一个布局文件然后add到DecorView上,如果在setContentView后面设置就起不到作用,所以有了这个设计。
2AppCompatActivity的setContentView
@Override public void setContentView(@LayoutRes int layoutResID) { getDelegate().setContentView(layoutResID); }
@NonNull public AppCompatDelegate getDelegate() { if (mDelegate == null) { mDelegate = AppCompatDelegate.create(this, this); } return mDelegate; }
实际上实现类是AppCompatDelegate,getDelegate().setContentView(layoutResID);的setContentView实际上是AppCompatDelegate的setContentView方法