现在的位置: 首页 > 综合 > 正文

Android学习札记35:onSaveInstanceState (Bundle outState)方法

2017年12月17日 ⁄ 综合 ⁄ 共 3621字 ⁄ 字号 评论关闭

PART 1:


首先看下官方文档的解释:



稍微翻一下:


在 Activity 被销毁之前被调用来保存每个实例的状态,这样就可以保证该状态能够从 onCreate(Bundle) 或者onRestoreInstanceState(Bundle)恢复过来。


本方法在 Activity 可能被销毁前调用,这样当该 Activity 在将来某个时刻重新回来时可以恢复其之前的状态。例如,如果 Activity B 启用后位于 Activity A 的前端,在某个时刻 Activity A 因为系统回收资源的原因要被销毁,Activity A
有机会通过 onSaveInstanceState() 来保存其用户界面状态,使得将来用户返回到 Activity A 的时候能够通过 onCreate(Bundle) 或者onRestoreInstanceState(Bundle) 来恢复其界面状态。


不要将这个方法和 Activity 生命周期中的回调如 onPause() 或 onStop() 搞混淆了,onPause() 在 Activtiy 被放置到后台或者自行销毁时总会被调用,onStop() 在 Activity 被销毁时被调用。一个会调用 onPause() 和 onStop() 但不会触发 onSaveInstanceState() 的例子是当用户从 Activity
B 返回到 Activity A 时:没有必要调用 B 的 onSaveInstanceState(Bundle)方法,因为此时的 B 实例永远不会被恢复,因此系统会避免调用它。一个调用 onPause() 但不调用 onSaveInstanceState(Bundle) 方法的例子是当 Activity B 启动后处在 Activity A 的前端:如果在B的整个生命周期里 A 的用户界面状态都没有被破坏的话,系统是不会调用 Activity A 的onSaveInstanceState(Bundle)方法。


默认的实现负责了大部分 UI 实例状态的保存,采用的方式是调用 UI 层上每个拥有 id 的 view 的 onSaveInstanceState()方法 ,并且保存当前获得焦点的 view 的 id (所有保存的状态信息都会在默认的 onRestoreInstanceState(Bundle)
实现中恢复)。如果你覆写这个方法来保存额外的没有被各个view保存的信息,你可能想要在默认实现过程中调用或者自己保存每个视图的所有状态。


如果被调用,这个方法会在 onStop() 前被触发,但系统并不保证是否在 onPause() 之前或者之后触发。



PART 2:


Activity 中的 onSaveInstanceState() 方法和 onRestoreInstanceState() 方法并不是生命周期方法,它们不同于 onCreate()、onPause() 等生命周期方法,它们并不一定会被触发。当应用遇到意外情况(如:内存不足、用户直接按Home键),由系统销毁一个
Activity 时,onSaveInstanceState() 方法就会被调用
。但是当用户主动去销毁一个 Activity 时,例如在应用中按返回键,onSaveInstanceState() 方法就不会被调用。因为在这种情况下,用户的行为决定了不需要保存Activity的状态。通常onSaveInstanceState() 方法只适合用于保存一些临时性的状态,而onPause() 方法适合用于数据的持久化保存。


另外,当屏幕的方向发生了改变, Activity 会被销毁并重新创建,如果你想在 Activity 被销毁前缓存一些数据,并且在 Activity 被重新创建后恢复缓存的数据。可以重写 Activity 中的 onSaveInstanceState() 方法和 onRestoreInstanceState()方法,如下: 

public class PreferencesActivity extends Activity {
	
	private String name;

	protected void onRestoreInstanceState(Bundle savedInstanceState) {
		// 重新创建后恢复缓存的数据
		name = savedInstanceState.getString("name");
		super.onRestoreInstanceState(savedInstanceState);
	}

	protected void onSaveInstanceState(Bundle outState) {
		// 被销毁前缓存一些数据
		outState.putString("name", "l_yqing");
		super.onSaveInstanceState(outState);
	}
}


PART 3:


当用户启动一个新 Activity 之后,之前的 Activity 可能在内存中处于停止状态也可能由于新 Activity 需要更多内存而被系统销毁了,但不论怎样,当用户在新 Activity 上点击返回键时,他希望看到的是原先的 Activity 的界面。原先的 Activity 如果是被重新创建的,那么它就要恢复到用户最后看到它时的样子,我们该怎么做呢?其实也不难,在 onPause()
、onStop() 或 onDestroy() 中保存必要的数据就行了。但是现在Google又冒出一个新的东西:onSaveInstanceState(),观其名可知其意:它是专门用来保存实例状态的,这个“实例”不是指的 Activity 对象,而是它所在的进程,因为Activity 的销毁是因为它所在的进程被杀掉而造成的。onSaveInstanceState()是在系统感觉需要销毁Activity时调用的,它被传入一个参数Bundle,这个Bundle可以被认为是个 Map 字典之类的东西,用“键-值”的形式来保存数据。


现在又叫人蛋疼了:不是可以在 onPause() 中保存数据吗?为什么又搞出这样一个家伙来?它们之间是什么关系呢?
原来,onSaveInstanceState() 方法的主要目的是保存和 Activity 的状态有关的数据,当系统在销毁 Activity 时,如果它希望 Activity 下次出现的样子跟之前完全一样,那么它就会调用onSaveInstanceState(),否则就不调用。所以要明白这一点:onSaveInstanceState() 方法并不是永远都会调用。比如,当用户在一个 Activity 点击返回键时,就不会调用,因为用户此时明确知道这个
Activity 是要被销毁的,并不期望下次它的样子跟现在一样(当然开发者可以使它保持临死时的表情,你非要这样做,系统也没办法),所以就不用调用onSaveInstanceState()。现在应该明白了:在onPause()、onStop() 以及 onDestroy() 中需要保存的是那些需要永久化的数据,而不是保存用于恢复状态的数据,状态数据有专门的方法:onSaveInstanceState()。数据保存在一个
Bundle 中,Bundle 被系统永久化。当再调用 Activity 的onCreate()时,原先保存的 Bundle就被传入,以恢复上一次临死时的模样,如果上次被销毁时没有保存 Bundle,则为 null。


还没完呢,如果你没有实现自己的 onSaveInstanceState(),但是 Activity 上控件的样子可能依然能被保存并恢复。原来 Activity 类已实现了onSaveInstanceState(),在 onSaveInstanceState() 的默认实现中,会调用所有控件的相关方法,把控件们的状态都保存下来,比如 EditText 中输入的文字、CheckBox 是否被选中等等。然而不是所有的控件都能被保存,这取决于你是否在
layout 文件中为控件赋了一个名字(android:id)。有名的就存,无名的不管。


既然有现成的可用,那么我们到底还要不要自己实现 onSaveInstanceState() 方法呢?这就得看情况了,如果你自己的派生类中有变量影响到UI,或你程序的行为,当然就要把这个变量也保存了,那么就需要自己实现,否则就不需要,但大多数情况肯定需要自己实现一下下了。对了,别忘了在你的实现中调用父类的 onSaveInstanceState() 方法。

注:由于 onSaveInstanceState() 方法并不是在每次被销毁时都会调用,所以不要在其中保存那些需要永久化的数据,执行保存那些数据的最好地方是在 onPause() 方法中。



参考资料:

http://blog.csdn.net/qjbagu/article/details/6743076

http://www.apkbus.com/forum.php?mod=viewthread&tid=13470&fromuid=3402

http://my.oschina.net/keeponmoving/blog/62107










抱歉!评论已关闭.