Activity的生命周期及常见回调顺序

Posted

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了Activity的生命周期及常见回调顺序相关的知识,希望对你有一定的参考价值。

参考技术A

关于Activity的生命周期相信不少人都觉得自己很了解了,毕竟大多数入门的时候都会首先学这个,但事实真的是这样吗?当初我也是这样想,直到在面试的时候栽了跟头~ 在哪跌倒就在哪爬起来,本文带你全面的了解一下Activity的生命周期,面试再问到也不怕啦

首先按照官方生命周期流程图逐个解释生命周期回调

当Activity首次创建的时候触发,这是生命周期的第一个方法,我们需要重写这个回调,并在调用setContentView()去加载界面布局资源,以及实例化一些变量。该方法有一个参数savedInstanceState,该参数包含Activity先前保存状态的Bundle对象。如果Activity是由于异常情况(切换横竖屏,内存不足等)被杀掉的,则Bundle对象的值不为null,否则Bundle的值为null。所以如果想要在onCreate()中做恢复工作需要对Bundle判断是否为空。不过建议在onRestoreInstanceState()方法中做恢复工作,因为该方法一旦被调用则Bundle不会为空。

表示Activity正在重新启动。一般情况下,当当前Activity从不可见重新变为可见状态时,onRestart就会被调用。这种情形一般是用户行为导致的,比如用户按Home键切换到桌面或打开了另一个新的Activity,接着用户又回到了这个Actvity。

表示Activity正在被启动,即将开始,这时Activity已经出现了,但是还没有出现在前台,无法与用户交互。这个时候可以理解为Activity已经显示出来,但是我们还看不到。

相信很多人会有疑问,什么叫Activity已经显示出来了,但是看不到???因为我们能看到的都是View或者和View有关联的东西,而Activity确实是没有什么直观的东西让我们看到的。所以这里说的看不到其实是看不到View,因为此时View还不是可见的,只有在onResume的时候才会把View设置为可见,想了解清楚的可以查看 Window和WindowManager的创建与Activity

此时Activity进入前台,具备与用户交互的能力。与onStart()都是对用户可见,但是onStart()时Activity是位于后台的,onResume()时Activity才显示到前台。 如果我们在onPause()中释放了组件,我们应该在onResume()中重新初始化。 我们应该在这里开始动画和初始化一些只有用户关注时才使用的组件。

当用户离开了Activity时会回调这个方法,比如说:

我们可以在这个方法中执行一些释放资源的操作,比如broadcast receivers,sensors,以及一些用户在不交互时不需要的资源。
该方法执行的时间非常简短,因此不应该在这个方法保存程序或用户数据;在该方法完成之前,保存工作可能并未完成。而且会影响到新的Activity的显示,因为onPause()执行完,新Activity才会开始执行onCreate()。

当Activity对用户不再可见的时候会回调该方法,比如说启动一个新的Activity把屏幕全都遮住了。
之前说过不建议在onPause()做一些重量级的释放工作,应该交由onStop()去释放大多数资源。比如unregisterReceiver(),还有其他可能导致内存泄露的资源,因为系统可能在没有回调onDestroy()的时候就杀掉了进程。

在Activity被销毁之前会回调该方法,这也是Activity生命周期的最后一个回调。
如果在Activity调用了finish()或者由于内存紧张系统销毁了该Activity,也有可能是由于横竖屏的切换或者用户按了返回键都会导致该方法的回调,
我们应该在这个方法中回收之前没有回收掉的所有资源

ActivityA:onPause() ->ActivityB:onCreate()->ActivityB:onStart()->ActivityB:onResume()-> ActivityA:onStop()

当ActivityA调用完onPause()之后才会调用ActivityB的onCreate(),所以不要在onPause()中做耗时操作,会影响新Activity的显示。

只有当ActivityB的onResume()调用完之后才会调用ActivityA的onStop(),因为此时ActivityB在最上面,ActivityA才是完全不可见的。

onPause()->onSaveInstanceState()->onStop()->onDestroy()->onCreate()->onStart()->onRestoreInstanceState()->onResume()

如果没有在androidManifest设置configChanges,则屏幕旋转的时候会吧Activity销毁再重建,并且重建后的Activity和原来的不是同一个实例。

如果设置了android:configChanges="orientation|screenSize",则屏幕旋转时只会调用onConfigurationChanged()方法,并且不会销毁重建

首先每个应用最少有一个进程,我们的Activity都是运行在进程中的,当应用退到后台时,系统可能因为内存不足而杀掉优先级低的进程,那么再该进程中的Activity都会被杀死,这种情况是不会走Activity的生命周期的,只有杀掉单个Activity的时候才会走Activity的onDestroy()方法。

onPause()->onStop()->onDestroy()

当Activity弹出Dialog获取PopupWindow是不会走任何生命周期的。

弹出DialogActivity原来的Activity会执行onPause(),关闭DialogActivity原来的Activity执行onResume()

onPause()->onStop()->onRestart()->onStart()->onResume()

onPause()->onNewIntent()->onResume()

Activity常见问题

Activity常见问题

通过这篇博客,我们能知道以下问题:

  1. Activity 各种情况下的生命周期
  2. 弹出 DialogActivity 生命周期有什么影响?
  3. onActivityResult() 在哪两个生命周期之间回调?
  4. ActivityonResume() 之后才显示的原因是什么?
  5. 通过 Sheme 协议打开 Activity
  6. Activity 什么时候会发生重建?
  7. ActivityonCreate() 方法里写死循环会 ANR 吗?

1. Activity 生命周期方法相关

生命周期的回调方法

Activity生命周期的回调主要有 onCreate()onRestart()onStart()onResume()onPause()onStop()onDestory() 几个方法。

调用时机说明:

  • onCreate():系统创建 Activity 时触发,用来初始化 Activity 的基本组件。
  • onRestart():当处于“已停止”状态的 Activity 即将重启时,系统就会调用此回调。
  • onStart()Activity 将进入“已启动”状态,并对用户可见。
  • onResume():系统会在 Activity 开始与用户互动之前调用此回调。此时,该 Activity 位于 Activity 堆栈的顶部,并会捕获所有用户输入
  • onPause():当 Activity 失去焦点并进入“已暂停”状态时,系统就会调用此回调。
  • onStop():当 Activity 对用户不再可见时,系统就会调用此回调。
  • onDestory():系统会在销毁 Activity 之前调用此回调。此回调是 Activity 接收的最后一个回调。通常,实现 onDestroy() 是为了确保该 Activity 的所有资源。

onStart()onStop() 是从 Activity 是否可见来回调的,onResume()onPause() 是从 Activity 是否位于前台来回调的。

在生命周期里几种状态:

  • Activity 的整个生命周期发生在 onCreate()onDestroy() 之间,在 onCreate() 中执行“全局”状态设置(例如状态布局),并在 onDestroy() 中释放资源。

  • Activity 的可见生命周期发生在 onStart()onStop() 之间,在这段时间内Activity对用户可见。在整个生命周期中,当 Activity 在对用户可见和隐藏俩种状态中交替变化时,系统会多次调用 onStart()onStop()

  • Activity 的前台生命周期发生在 onResume()onPause() 之间,Activity位于其他 Activity 之前(栈顶位置),可与用户交互并具有输入焦点。但状态改变频繁,系统会多次调用 onResume()onPause(),建议做些轻量级操作。

打开Activity时两个Activity的生命周期方法的回调顺序

当一个 Activity 启动另一个 Activity 时,它们都会经历生命周期转换。第一个 Activity 停止运行并进入“已暂停”或“已停止”状态,同时创建另一个 Activity

  • Activity A 启动 Activity B,B ActivitylaunchModestandard 或者 B Activity 没有可复用的实例时: A.onPause() -> B.onCreate() -> B.onStart() -> B.onResume() -> A.onStop() --> A.onDestory()(如果需要关闭Activity A[A 被移出栈])

  • Activity A 启动 Activity B,B ActivitylaunchModesingleTop且 B Activity 已经在栈顶时(一些特殊情况如通知栏点击、连点、B Activity自己打开自己),此时只有 B 页面自己有生命周期变化:B.onPause() -> B.onNewIntent() -> B.onResume()

  • 当 B ActivitylaunchModesingleInstancesingleTask 且对应的 B Activity 有可复用的实例时:A.onPause() -> B.onNewIntent() -> B.onRestart() -> B.onStart() -> B.onResume() -> A.onStop() --> A.onDestory()(如果需要关闭Activity A[A 被移出栈])

  • 当 B ActivityThemeDialog 时:A.onPause() -> B.onCreate() -> B.onStart() -> B.onResume()(注意前一个 Activity 不会回调 onStop(),因为只有在 Activity 切到后台(不在Activity栈顶)不可见才会回调 onStop();而弹出 Dialog 主题的 Activity 时前一个页面还是可见的,只是失去了焦点而已所以仅有 onPause() 回调)

2. 弹出 DialogActivity 生命周期有什么影响?

**先下结论:**通过 《Android Activity——启动过程探索(一)》 《Android Activity——启动过程探索(二)》 《Android Activity——启动过程探索(三)》 我们知道Activity生命周期回调都是通过ActivityTaskManagerService(ATMS)(在Android 10之前是通过 ActivityManagerService(AMS))来回调的。但是弹出 DialogToastPopupWindow 本质上都直接是通过 WindowManager.addView() 显示的(没有经过 ATMS/AMS),所以不会对生命周期有任何影响。

Dialog显示过程源码解析

源码版本为 Android 10(Api 29),不同Android版本可能有一些差别

  1. 构造方法中获取 WindowManager 和创建 PhoneWindow 并通过 PhoneWindowsetWindowManager() 方法关联

     public Dialog(@NonNull Context context, @StyleRes int themeResId) 
         this(context, themeResId, true);
     
    
     Dialog(@NonNull Context context, @StyleRes int themeResId, boolean createContextThemeWrapper) 
     	// 获取 WindManager,实际为实现类 WindowManagerImpl
         mWindowManager = (WindowManager) context.getSystemService(Context.WINDOW_SERVICE);
         // 创建 PhoneWindow 对象
     	final Window w = new PhoneWindow(mContext);
         mWindow = w;
         w.setCallback(this);
         w.setOnWindowDismissedCallback(this);
         w.setOnWindowSwipeDismissedCallback(() -> 
             if (mCancelable) 
                 cancel();
             
         );
         w.setWindowManager(mWindowManager, null, null);
         w.setGravity(Gravity.CENTER);
    
         mListenersHandler = new ListenersHandler(this);
     
    
  2. Dialog#show() 方法源码实现

     public void show() 
         mCanceled = false;
     
         if (!mCreated) 
             dispatchOnCreate(null); // 没有创建过的话,调用 onCreate() 方法
          else 
             // 创建过的话,就不在调用 onCreate() 回调,只是修改配置
             final Configuration config = mContext.getResources().getConfiguration();
             mWindow.getDecorView().dispatchConfigurationChanged(config);
         
     
         onStart(); // 调用 onStart() 回调方法
         mDecor = mWindow.getDecorView(); // 通过 PhoneWindow 获取 DecorView
     
         ... // 省略,设置默认logo、icon以及确定软键盘模式等
     
         // 通过 WindowManager(实际为WindowManagerImpl对象)将DecorView增加窗体上
         mWindowManager.addView(mDecor, l);
         
         ... // 设置软键盘模式
     
         mShowing = true; // 修改显示标记为 true
     
         sendShowMessage();
     
    
     // WindowManagerImpl 的 addView() 方法
     @Override
     public void addView(@NonNull View view, @NonNull ViewGroup.LayoutParams params) 
         applyDefaultToken(params);
         // 调用 WindowManagerGlobal 的 addView() 方法
         mGlobal.addView(view, params, mContext.getDisplay(), mParentWindow);
     
     
     // WindowManagerGlobal 的 addView() 方法核心代码
     public void addView(View view, ViewGroup.LayoutParams params,
             Display display, Window parentWindow) 
         
         ViewRootImpl root;
         View panelParentView = null;
     
         synchronized (mLock) 
         	// 创建 ViewRootImpl 对象
             root = new ViewRootImpl(view.getContext(), display);
             view.setLayoutParams(wparams);
             // 调用 ViewRootImpl 的 setView() 方法
             root.setView(view, wparams, panelParentView);
         
     
     
     // ViewRootImpl 的 setView() 方法核心代码
     public void setView(View view, WindowManager.LayoutParams attrs, View panelParentView) 
         synchronized (this) 
             if (mView == null) 
                 mView = view;
                 // 调用 requestLayout() 方法,进行布局(包括measue、layout、draw)
                 requestLayout();
                 
                 mOrigWindowType = mWindowAttributes.type;
                 mAttachInfo.mRecomputeGlobalAttributes = true;
                 collectViewAttributes();
                 // 通过调用 Session 的 addToDisplay() 方法
                 res = mWindowSession.addToDisplay(mWindow, mSeq, mWindowAttributes,
                         getHostVisibility(), mDisplay.getDisplayId(), mTmpFrame,
                         mAttachInfo.mContentInsets, mAttachInfo.mStableInsets,
                         mAttachInfo.mOutsets, mAttachInfo.mDisplayCutout, mInputChannel,
                         mTempInsets);
                 setFrame(mTmpFrame);
             
         
     
    

ViewRootImplsetView() 方法说明:

  1. mWindowSession 是通过 WindowManagerGlobalgetWindowSession() 方法获取到的,返回的是通过 WindowManagerServiceopenSession() 方法创建的 Session 对象

  2. mWindow 对象为 ViewRootImpl 的 内部类 static class W extends IWindow.Stub,通过定义可以看出来,是一个 Binder 对象

  3. Session 对象的 addToDisplay() 方法

     Override
     public int addToDisplay(IWindow window, int seq, WindowManager.LayoutParams attrs,
             int viewVisibility, int displayId, Rect outFrame, Rect outContentInsets,
             Rect outStableInsets, Rect outOutsets,
             DisplayCutout.ParcelableWrapper outDisplayCutout, InputChannel outInputChannel,
             InsetsState outInsetsState) 
          // mService 为 WindowManagerService,window 就是上面的 `ViewRootImpl` 的 内部类 `static class W extends IWindow.Stub`
         return mService.addWindow(this, window, seq, attrs, viewVisibility, displayId, outFrame,
                 outContentInsets, outStableInsets, outOutsets, outDisplayCutout, outInputChannel,
                 outInsetsState);
     
    

通过这个方法就能够实现客户端与 WindowManagerService 的双向调用了(应用端通过 mWindowSession 调用 WMS, WMS 通过 mWindow (一个 Binder 对象) 调用应用端)

DialogFragment源码解析

注意:该部分源码来自 support v4 包

DialogFragment 的定义

class DialogFragment extends Fragment

发现就是直接继承了 Fragment,那么就可以肯定,Fragment 有的 DialogFragment 都有,像生命周期方法等,又因为我们可以通过 getDialog() 方法获取到 Dialog 。所以可以肯定它包含一个了 Dialog。接着我们先看 show() 方法

public void show(FragmentManager manager, String tag) 
    mDismissed = false;
    mShownByMe = true;
    FragmentTransaction ft = manager.beginTransaction();
    ft.add(this, tag);
    ft.commit();

发现就是将 DialogFragment 显示出来,那我们接着找一下,看看 Dialog 是在哪里创建的。发现如下代码:

@Override
public LayoutInflater getLayoutInflater(Bundle savedInstanceState) 
    mDialog = onCreateDialog(savedInstanceState);
    return (LayoutInflater) mActivity.getSystemService(
            Context.LAYOUT_INFLATER_SERVICE);


@NonNull
public Dialog onCreateDialog(Bundle savedInstanceState) 
    return new Dialog(getActivity(), getTheme());

发现是在 getLayoutInflater() 方法中创建的,那么这个 getLayoutInflater() 方法又是在哪里被调用的呢?通过查找我们发现这个方法是在 FragmentManager 中调用的,调用完这个方法之后就会调用 FragmentonViewCreated() 方法;而 方法的返回值 LayoutInflater 就是 Fragment 中回调方法 onCreateView() 的参数。

分析到这里,我们就知道了,DialogFragment 实际上是一个 Fragment ,只是他在 onViewCreated() 方法之前通过 getLayoutInflater() 方法创建了一个 Dialog,也就是一个普通的 Fragment 中包含了一个 Dialog。那么又出现了问题,我们刚刚看了 show() 方法,发现并没有调用 Dialog。show() 方法,那么 Dialog 是怎么显示的呢?我们前面就说过,DialogFragment 实际上是一个 FragmentFragment 有的 DialogFragment 都有;我们都知道Fragment有一个生命周期方法 onStart() 是在用户可见时被调用的,那么 Dialog.show() 方法是否在该方法种调用了,看一下代码:

@Override
public void onStart() 
    super.onStart();
    if (mDialog != null) 
        mViewDestroyed = false;
        mDialog.show();
    

果然,在 Fragment 的生命周期方法 onStart()回调中,调用了 Dialog.show() 方法,那么当我么调用 DialogFragmentshow() 方法时,把将 DialogFragment 显示出来了,那么 Fragment 的生命周期方法 onStart() 自然会被回调,Dialog 自然也就显示出来了。Fragment 的生命周期方法回调并非由 ActivityManagerService(AMS)【Android 10 以上 ActivityTaskManagerService(ATMS)】来控制,而是通过 FragmentManager 回掉的,也不会影响到 Activity 的生命周期。

3. onActivityResult 在哪两个生命周期之间回调?

onActivityResult 不属于 Activity 的生命周期。但是答案很简单,因为在 onActivityResult() 方法的注释中就写着答案:「You will receive this call immediately before onResume() when your activity is re-starting.」 所以 onActivityResult() 回调先于该 Activity 的所有生命周期回调,从 B Activity 返回 A Activity 的生命周期调用为:

B.onPause() -> A.onActivityResult() -> A.onRestart() -> A.onStart() -> A.onResume()

4. Activity 在 onResume 之后才显示的原因是什么?

通过《Android自定义View之Activity页面的组成》我们知道了在 Activity 中调用 setContentView() 方法会将我们的 View 添加到 DecorView 中,DecorView 也是一个控件,他要加载到界面上让用户可见,是通过 Window (也就是 PhoneWindow)来完成的,所以我们只要知道 PhoneWindow 是在什么时候将 DecorView 加载出来的,就知道了用户是在什么时候才可见布局信息。那么,到底是什么时候开始将 DecorView 添加到 PhoneWindow 中的了, 在 《Android Activity——启动过程探索(二)》 中,我们说到了 Activity 的回调 onResume() 方法,中间有一段是会调用 ActivityThread#handleResumeActivity() 方法,我们来看一下这个方法的源码(省略大部分代码之后的核心代码):

@Override
public void handleResumeActivity(IBinder token, boolean finalStateRequest, boolean isForward, String reason) 
	// 回调 onResume() 方法
    final ActivityClientRecord r = performResumeActivity(token, finalStateRequest, reason);
   
    final Activity a = r.activity;
    if (r.window == null && !a.mFinished && willBeVisible) 
        r.window = r.activity.getWindow(); // 获取PhoneWindow并赋值
        View decor = r.window.getDecorView(); // 获取 DecorView 控件
        decor.setVisibility(View.INVISIBLE);
		// 获取ViewManager,也就是WindowManager(因为interface WindowManager extends ViewManager)
        ViewManager wm = a.getWindowManager(); 
        
        a.mWindowAdded = true;
        wm.addView(decor, l); // 关键点
    

方法说明:

  1. ViewManager wm = a.getWindowManager() 方法最终返回的是 WindowManagerImpl 对象,原因如下:

    1. Activity.getWindowManager() 方法返回 Activity 的成员变量 mWindowManager
    2. Activity 的成员变量 mWindowManagerActivityattach() 方法中被赋值 mWindowManager = mWindow.getWindowManager(),调用的是 WindowgetWindowManager() 方法
    3. WindowgetWindowManager() 方法返回的是他的成员变量 mWindowManager
    4. Window 的成员变量 mWindowManager 在他的 setWindowManager() 方法中被赋值 mWindowManager = ((WindowManagerImpl)wm).createLocalWindowManager(this) ,所以得出结论 a.getWindowManager() 方法最终返回的是 WindowManagerImpl 对象
  2. 关键点 wm.addView(decor, l) 调用的就是 WindowManagerImpl#addView() 方法了,之后的调用过程在当前文章前面【Dialog#show() 方法源码实现】 部分已经说过了,就不在重复了。

通过以上分析,我们就知道了实际上 Activity 中的布局内容是在 onResume() 回调之后才挂载到 Window 上的,也就是这个时候才是对用户可见的。

5. 通过 URL Sheme 协议打开 Activity

URL Scheme 是一种页面内跳转协议,通过这个协议可以比较方便的跳转到app某一个页面,也可以通过 h5 打开App页面。

URL Sheme 的格式:[scheme]😕/[host][:port]/[path]?[query]

  • scheme:协议名称 [scheme]😕/ 为必填,其他为非必填
  • host:主机名(域名)
  • port:端口号
  • path:路径
  • query:参数(多个参数之间用 & 分割,类似 get 请求)

使用

1. APP端需要能通过 sheme 打开的 Activity 进行配置 intent 过滤器(在清单文件中配置)

	<activity android:name=".TestActivity">
	    <!--Android 接收外部跳转过滤器-->
	    <!--要想在别的App上能成功调起App,必须添加intent过滤器-->
	    <intent-filter>
	        <!-- 协议部分配置 ,注意需要跟web配置相同-->
	        <data
	            android:host="action.test.activity"
	            android:pathPrefix="/test"
	            android:port="999"
	            android:scheme="testapp" />
	
	        <!--下面这几行也必须得设置-->
	        <action android:name="android.intent.action.VIEW" />
	
	        <category android:name="android.intent.category.DEFAULT" />
	        <category android:name="android.intent.category.BROWSABLE" />
	    </intent-filter>
	</activity>

data属性说明:

  • scheme: 协议名称,必填,其他为非必填(由开发人员自定义)

  • host: 域名

  • port:端口

  • path: 完整的路径,如:http://example.com/blog/abc.html,这里将 path 设置为 /blog/abc.html 才能够进行匹配

  • pathPrefix:路径的开头部分,拿上来的 Uri 来说,这里将 pathPrefix 设置为 /blog 就能进行匹配了

  • pathPattern:用表达式来匹配整个路径,匹配符如下:

    • ” 用来匹配0次或更多,如:“a” 可以匹配“a”、“aa”、“aaa”…
    • “.” 用来匹配任意字符,如:“.” 可以匹配“a”、“b”,“c”…
    • 因此 “.*” 就是用来匹配任意字符0次或更多

2. 在 Activity 获取 sheme 信息:

	Uri uri = getIntent().getData();
    if (uri != null) 
        // 完整的url信息
        Log.i(TAG, "url:" + uri);

        // scheme部分
        String scheme = uri.getScheme();
        Log.i(TAG, "scheme:" + scheme);

        // host部分
        String host = uri.getHost();
        Log.i(TAG, "host:" + host);

        // port部分
        int port = uri.getPort();
        Log.i(TAG, "port:" + port);

        // 访问路劲
        String path = uri.getPath();
        Log.i(TAG, "path:" + path);
        List<String> pathSegments = uri.getPathSegments();
        Log.i(TAG, "pathSegments:" + pathSegments);

        // Query部分
        String query = uri.getQuery();
        Log.i(TAG, "query:" + query);

        //获取指定参数值
        String params1 = uri.getQueryParameter("params1");
        String params2 = uri.getQueryParameter("params2");
        Log.i(TAG, "params1:" + params1 + "  params2:" + params2);
    

3. 打开 Activity 的方式

1. 通过原生代码打开

		String uri = "testapp://action.test.activity:999/test?params1=1&params2=true";
        Intent intent = new Intent(Intent.ACTION_VIEW, Uri.parse(uri));
        startActivity(intent);

1. 通过h5代码打开(使用浏览器打开包含如下代码的 html 文件)

		<html>
			<title>测试</title>
		
			<body>
				 <a href="testapp://action.test.activity:999/test?params1=1&params2=true">打开APP</a>
			</body>
		</html>

6. Activity 什么时候会发生重建?

发生时机:

  1. 系统资源回收:内存不足,低优先级的Activity会被杀死
  2. 配置发生变化:当系统配置发生变化时,比如屏幕方向、语言的改变

横竖屏切换不销毁重建的方式

在清单文件中为该 Activity 配置 android:configChanges 属性。属性值 orientation|screenSize(Api级别为13以下时只需要配置 orientation 即可) 对应着旋转屏幕,locale 对应着语言变化。如此,在配置发生变化时,不会导致重建,而是走 onConfigurationChanged() 回调方法。

保存和恢复住状态

保存和恢复状态分别通过 onSaveInstanceState() 方法和 onRestoreInstanceState() 方法实现。

  1. ActivityonSaveInstanceState() 执行在 onStop() 之前,与 onPause() 没有固定的时序关系;onRestoreInstanceState() 执行在 onStart() 之后。不止 Activity 有这两个方法,每个 View 也有这两个方法,用来保存 View 的信息。
  2. 正常情况下的活动销毁并不会调用这两个方法,只有当活动异常销毁并且有机会重现展示的时候才会进行调用。
  3. ActivityonRestoreInstanceState()onCreate() 都可以进行数据恢复工作,但建议采用在 onRestoreInstanceState() 中去恢复(因为调用了这个方法,用来存取数据的 Bundle 肯定不为 null)。
  4. onSaveInstanceState()onRestoreInstanceState() 这两个方法中,系统会默认为我们进行一定的恢复工作,比如:会为布局中的每个 View 调用相应的 onSaveInstanceState() 方法,让每个视图都能提供有关自身的应保存信息。

具体调用时机说明

Activity#onSaveInstanceState() 方法调用时机:

  1. 当用户按下HOME键时
  2. 从最近应用中选择运行其他的程序时
  3. 按下电源按键(关闭屏幕显示)时
  4. 从当前activity启动一个新的activity时
  5. 屏幕方向切换时(无论竖屏切横屏还是横屏切竖屏都会调用)
  6. 被系统回收时

Activity#onRestoreInstanceState() 方法调用时机:

  1. 被系统回收且重建时
  2. 屏幕方向切换时(无论竖屏切横屏还是横屏切竖屏都会调用)
  3. 语言切换时

7. 在 Activity 的 onCreate() 方法里写死循环会 ANR 吗?

ANR全称:Application Not Responding,也就是应用程序无响应

Android 中产生 ANR 的原因:

  1. Service TimeOut: service 未在规定时间执行完成:前台服务 20s,后台 200s
  2. BroadCastQueue TimeOut: 未在规定时间内未处理完广播:前台广播 10s 内, 后台 60s 内
  3. ContentProvider TimeOut: publish 在 10s 内没有完成
  4. Input Dispatching timeout: 5s 内未响应键盘输入、触摸屏幕等事件

根据以上 ANR 产生的原因,说明在 ActivityonCreate() 方法中写死循环是不会发生ANR的,但是主线程被死循环一直占用了,所以当再有其他事件产生时,就不能及时响应了,从而导致ANR发生。

以上是关于Activity的生命周期及常见回调顺序的主要内容,如果未能解决你的问题,请参考以下文章

如何理解Activity 的生命周期

Android——Activity和Intent及Activity的生命周期

关于android应用activity生命周期中,按下home键和按下back键的问题

Activity 生命周期回调可以相互中断还是同时发生?

跟着Google学Android —— 3.1 管好Activity的生命周期

Activity常见问题