Android开发的相关程序员都知道,如果需要在Activity页面完成对一些临时的、非永久数据存储并进行恢复时,方法之一是通过Activity中的onSaveInstanceState(Bundle outState)方法保存,然后在onRestoreInstanceState(Bundle savedInstanceState)方法中恢复之前的临时数据,并且如果调用onSaveInstanceState()方法的话, 调用将发生在onPause()或onStop()方法之前。
其实,即使开发者没有实现onSaveInstanceState方法,Activity中的一些UI空间状态也会被保存,主要是由于Android应用框架中定义的几乎所有UI控件都默认实现了onSaveInstanceState()方法, 因此当activity被动的摧毁和重建时, 这些UI控件会自动保存和恢复状态数据. 比如EditText编辑控件,它会自动保存和恢复输入的数据, 而CheckBox选中控件也会自动保存和恢复选中状态. 因此,作为开发者只需要为这些控件指定一个唯一的Android ID, 保存、恢复的数据工作就会自动完成。如果没有为控件指定ID, 那么控件就不会进行自动的数据保存和恢复操作。
复写View控件的onSaveInstanceState方法的使用场景,如果需要自定义扩展控件时,继承原生Android控件并添加了额外的成员属性,那么此时保存额外的数据时, 就需要覆写onSaveInstanceState()方法。具体操作实例如下:
具体场景为:在系统设置里,进行来电铃声设置时,使用了系统原生控件RingtonePreference.java类进行自定义拓展为 CustomRingtonePreference.java,但是在一些低内存配置手机上会出现,Settings进程会被动杀死,导致铃声选择preference被初始化为默认卡1对象,导致点击“确定”保存铃声时,将选择的铃声保存为卡1的铃声。这样,假如在我们设置的是卡2来电铃声时,出现低内存被杀死,状态没有保存(就是我们选择的卡1还是卡2?),重建时也无法恢复,就会默认到选择的是卡1铃声。具体代码修复如下:
import android.preference.RingtonePreference;
import android.provider.Settings;
import android.util.AttributeSet;
+import android.os.Parcel;
+import android.os.Parcelable;
import android.provider.MediaStore;
import java.io.IOException;
@@ -199,4 +201,52 @@ public class CustomRingtonePreference extends RingtonePreference {
mPhoneCount = TelephonyManager.getPhoneCount();
}
// 保存数据状态
+ protected Parcelable onSaveInstanceState() {
+ final Parcelable superState = super.onSaveInstanceState();
+ SavedState state = new SavedState(superState);
+ state.phoneId = mPhoneId;
+ return state;
+ }
+ // 恢复数据状态
+ protected void onRestoreInstanceState(Parcelable state) {
+ if (state == null || !state.getClass().equals(SavedState.class)) {
+ // Didn't save state for us in onSaveInstanceState
+ super.onRestoreInstanceState(state);
+ return;
+ }
+
+ SavedState myState = (SavedState) state;
+ super.onRestoreInstanceState(myState.getSuperState());
+ mPhoneId = myState.phoneId;
+ }
+ // 继承 BaseSavedState 类
+ private static class SavedState extends BaseSavedState {
+ int phoneId;
+
+ public SavedState(Parcelable superState) {
+ super(superState);
+ }
+
+ public SavedState(Parcel source) {
+ super(source);
+ phoneId = source.readInt();
+ }
+
+ @Override
+ public void writeToParcel(Parcel dest, int flags) {
+ super.writeToParcel(dest, flags);
+ dest.writeInt(phoneId);
+ }
+
+ public static final Parcelable.Creator<SavedState> CREATOR = new Parcelable.Creator<SavedState>() {
+
+ public SavedState createFromParcel(Parcel in) {
+ return new SavedState(in);
+ }
+
+ public SavedState[] newArray(int size) {
+ return new SavedState[size];
+ }
+ };
+ }
}
最后,在使用时请注意:由于onSaveInstanceState()方法方法不一定会被调用, 故不适合在该方法中保存持久化数据, 如向数据库中插入数据等。 保存持久化数据的操作应该放在onPause()中; onSaveInstanceState()方法只适合保存瞬态数据,比如UI控件的状态,成员变量的值等。
当然,除了系统处于内存不足,会摧毁activity之外, 某些系统设置的改变也会导致activity的摧毁和重建. 例如改变屏幕方向,改变设备语言设定,,键盘弹出等。