如何使用保存实例状态来保存活动状态
技术背景
在 Android 开发中,Activity 的状态保存是一个常见的需求。当 Activity 因系统内存不足被销毁,或者因屏幕旋转等配置更改而重新创建时,需要恢复之前的状态,如用户的输入、选择等。onSaveInstanceState
和 onRestoreInstanceState
方法就是为此设计的,但它们的使用场景和持久化存储有所不同。
实现步骤
1. 重写 onSaveInstanceState
方法
在 Activity 中重写 onSaveInstanceState
方法,将需要保存的状态信息写入 Bundle
对象。
1 2 3 4 5 6 7 8
| @Override public void onSaveInstanceState(Bundle savedInstanceState) { super.onSaveInstanceState(savedInstanceState); savedInstanceState.putBoolean("MyBoolean", true); savedInstanceState.putDouble("myDouble", 1.9); savedInstanceState.putInt("MyInt", 1); savedInstanceState.putString("MyString", "Welcome back to Android"); }
|
2. 重写 onRestoreInstanceState
方法或在 onCreate
中恢复状态
在 Activity 重新创建时,从 Bundle
中恢复状态信息。
1 2 3 4 5 6 7 8
| @Override public void onRestoreInstanceState(Bundle savedInstanceState) { super.onRestoreInstanceState(savedInstanceState); boolean myBoolean = savedInstanceState.getBoolean("MyBoolean"); double myDouble = savedInstanceState.getDouble("myDouble"); int myInt = savedInstanceState.getInt("MyInt"); String myString = savedInstanceState.getString("MyString"); }
|
或者在 onCreate
中恢复:
1 2 3 4 5 6 7 8 9 10
| @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); if (savedInstanceState != null) { boolean myBoolean = savedInstanceState.getBoolean("MyBoolean"); double myDouble = savedInstanceState.getDouble("myDouble"); int myInt = savedInstanceState.getInt("MyInt"); String myString = savedInstanceState.getString("MyString"); } }
|
3. 持久化存储
对于需要长期保存的状态,可使用 SQLite 数据库、文件或 SharedPreferences。
使用 SharedPreferences 示例:
1 2 3 4 5 6 7 8 9 10
| @Override protected void onPause() { super.onPause(); SharedPreferences preferences = getPreferences(MODE_PRIVATE); SharedPreferences.Editor editor = preferences.edit(); EditText txtName = (EditText) findViewById(R.id.txtName); String strName = txtName.getText().toString(); editor.putString("Name", strName); editor.commit(); }
|
核心代码
存储和恢复复杂对象
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28
| class MyModel implements Serializable { JSONObject obj;
public void setJsonObject(JSONObject obj) { this.obj = obj; }
public JSONObject getJsonObject() { return obj; } }
@Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); if (savedInstanceState != null) { MyModel data = (MyModel) savedInstanceState.getSerializable("yourkey"); JSONObject obj = data.getJsonObject(); } }
@Override protected void onSaveInstanceState(Bundle outState) { super.onSaveInstanceState(outState); MyModel dataToSave = new MyModel(); dataToSave.setJsonObject(obj); outState.putSerializable("yourkey", dataToSave); }
|
使用注解和反射简化状态保存
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104
| import java.lang.annotation.Documented; import java.lang.annotation.ElementType; import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; import java.lang.annotation.Target;
@Documented @Retention(RetentionPolicy.RUNTIME) @Target({ElementType.FIELD}) public @interface SaveInstance { }
import android.app.Activity; import android.app.Fragment; import android.os.Bundle; import android.os.Parcelable; import android.util.Log;
import java.io.Serializable; import java.lang.reflect.Field;
public class Icicle { private static final String TAG = "Icicle";
public static void save(Bundle outState, Object classInstance) { save(outState, classInstance, classInstance.getClass()); }
public static void save(Bundle outState, Object classInstance, Class<?> baseClass) { if (outState == null) { return; } Class<?> clazz = classInstance.getClass(); while (baseClass.isAssignableFrom(clazz)) { String className = clazz.getName(); for (Field field : clazz.getDeclaredFields()) { if (field.isAnnotationPresent(SaveInstance.class)) { field.setAccessible(true); String key = className + "#" + field.getName(); try { Object value = field.get(classInstance); if (value instanceof Parcelable) { outState.putParcelable(key, (Parcelable) value); } else if (value instanceof Serializable) { outState.putSerializable(key, (Serializable) value); } } catch (Throwable t) { Log.d(TAG, "The field '" + key + "' was not added to the bundle"); } } } clazz = clazz.getSuperclass(); } }
public static void load(Bundle savedInstanceState, Object classInstance) { load(savedInstanceState, classInstance, classInstance.getClass()); }
public static void load(Bundle savedInstanceState, Object classInstance, Class<?> baseClass) { if (savedInstanceState == null) { return; } Class<?> clazz = classInstance.getClass(); while (baseClass.isAssignableFrom(clazz)) { String className = clazz.getName(); for (Field field : clazz.getDeclaredFields()) { if (field.isAnnotationPresent(SaveInstance.class)) { String key = className + "#" + field.getName(); field.setAccessible(true); try { Object fieldVal = savedInstanceState.get(key); if (fieldVal != null) { field.set(classInstance, fieldVal); } } catch (Throwable t) { Log.d(TAG, "The field '" + key + "' was not retrieved from the bundle"); } } } clazz = clazz.getSuperclass(); } } }
public class MainActivity extends Activity { @SaveInstance private String foo;
@SaveInstance private int bar;
@Override public void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); Icicle.load(savedInstanceState, this); }
@Override public void onSaveInstanceState(Bundle outState) { super.onSaveInstanceState(outState); Icicle.save(outState, this); } }
|
最佳实践
- 对于临时状态,如屏幕旋转时的状态,使用
onSaveInstanceState
和 onRestoreInstanceState
。 - 对于持久化状态,如用户的设置、历史记录等,使用 SQLite 数据库或 SharedPreferences。
- 在
onPause
中保存持久化数据,以确保在各种情况下数据都能被保存。 - 可以使用第三方库,如 Icepick,简化状态保存和恢复的代码。
常见问题
1. savedInstanceState
为 null
Activity 只有在因配置更改(如屏幕旋转)或系统销毁后重新创建时,savedInstanceState
才会包含状态信息。如果是用户按返回键退出 Activity 后重新启动,savedInstanceState
通常为 null。
2. onSaveInstanceState
不被调用
onSaveInstanceState
不是 Activity 生命周期的必调用方法,当用户正常关闭 Activity 时,该方法不会被调用。因此,对于持久化数据,应在 onPause
中进行保存。
3. 使用 android:configChanges
的问题
在 AndroidManifest.xml
中使用 android:configChanges
来处理配置更改时,可能会导致一些意外问题,如屏幕显示异常。因此,建议优先使用 onSaveInstanceState
和 onRestoreInstanceState
来处理状态保存。