Android系统的回收机制会在未经用户主动操作的情况下销毁activity(比如长时间后台运行),而为了避免系统回收activity导致数据丢失,Android为我们提供了onSaveInstanceState(Bundle outState)和onRestoreInstanceState(Bundle savedInstanceState)用于保存和恢复数据。
当Activity后台被回收后,再次打开App,Acitivity会执行空参的构造函数,在该函数中的初始化操作会生效,而在Fragment中,再执行构造函数的同时会执行onAttach和onCreate方法,所以onAttach和onCreate中的初始化会生效。
一、onSaveInstanceState(Bundle outState)
当activity被系统自动回收的情况下,且是在onStop()之前会调用该方法,另外,如果activity是用户主动销毁,比如按下back返回键或者是调用finish方法,onSaveInstanceState就不会被调用。
总结下,onSaveInstanceState(Bundle outState)会在以下情况被调用:
1、当用户按下HOME键时。
2、从最近应用中选择运行其他的程序时。
3、按下电源按键(关闭屏幕显示)时。
4、从当前activity启动一个新的activity时。
5、屏幕方向切换时(无论竖屏切横屏还是横屏切竖屏都会调用)。
在这些情况下,该方法的调用时机都是在onPause和onStop方法之间:onPause -> onSaveInstanceState -> onStop。
二、onRestoreInstanceState (Bundle saveInstanceState)
onRestoreInstanceState(Bundle savedInstanceState)只有在activity确实是被系统回收,重新创建activity的情况下才会被调用。
比如第5种情况屏幕方向切换时,activity生命周期如下:
onPause -> onSaveInstanceState -> onStop -> onDestroy -> onCreate -> onStart -> onRestoreInstanceState -> onResume
在这里onRestoreInstanceState被调用,是因为屏幕切换时原来的activity确实被系统回收了,又重新创建了一个新的activity。
(顺便吐槽一下网上的那些文章说横屏切竖屏和竖屏切横屏时activity生命周期方法执行不一样,经自己实践证明是一样的。)
而按HOME键返回桌面,又马上点击应用图标回到原来页面时,activity生命周期如下:
onPause -> onSaveInstanceState -> onStop -> onRestart -> onStart -> onResume
因为activity没有被系统回收,因此onRestoreInstanceState没有被调用。
如果onRestoreInstanceState被调用了,则页面必然被回收过,则onSaveInstanceState必然被调用过。
三、onCreate()里也有Bundle参数,可以用来恢复数据,它和onRestoreInstanceState有什么区别?
因为onSaveInstanceState 不一定会被调用,所以onCreate()里的Bundle参数可能为空,如果使用onCreate()来恢复数据,一定要做非空判断。
而onRestoreInstanceState的Bundle参数一定不会是空值,因为它只有在上次activity被回收了才会调用。
而且onRestoreInstanceState是在onStart()之后被调用的。有时候我们需要onCreate()中做的一些初始化完成之后再恢复数据,用onRestoreInstanceState会比较方便。下面是官方文档对onRestoreInstanceState的说明:
- 1
注意这个说明的最后一句是什么意思?
to allow subclasses to decide whether to use your default implementation.
它是说,用onRestoreInstanceState方法恢复数据,你可以决定是否在方法里调用父类的onRestoreInstanceState方法,即是否调用super.onRestoreInstanceState(savedInstanceState);
而用onCreate()恢复数据,你必须调用super.onCreate(savedInstanceState);
否则运行会报如下错误:
E/AndroidRuntime(4964): android.util.SuperNotCalledException: Activity {com.example.test/com.example.test.SecondActivity} did not call through to super.onCreate()E/AndroidRuntime(4964): at android.app.ActivityThread.performLaunchActivity(ActivityThread.java:2331)E/AndroidRuntime(4964): at android.app.ActivityThread.handleLaunchActivity(ActivityThread.java:2426)E/AndroidRuntime(4964): at android.app.ActivityThread.access$800(ActivityThread.java:153)E/AndroidRuntime(4964): at android.app.ActivityThread$H.handleMessage(ActivityThread.java:1345)E/AndroidRuntime(4964): at android.os.Handler.dispatchMessage(Handler.java:110)E/AndroidRuntime(4964): at android.os.Looper.loop(Looper.java:193)E/AndroidRuntime(4964): at android.app.ActivityThread.main(ActivityThread.java:5386)E/AndroidRuntime(4964): at java.lang.reflect.Method.invokeNative(Native Method)E/AndroidRuntime( 4964): at java.lang.reflect.Method.invoke(Method.java:515)E/AndroidRuntime(4964): at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:829)E/AndroidRuntime(4964): at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:645)E/AndroidRuntime(4964): at dalvik.system.NativeStart.main(Native Method)--------- beginning of /dev/log/main