如何使用保存实例状态来保存活动状态

如何使用保存实例状态来保存活动状态

技术背景

在 Android 开发中,Activity 的状态保存是一个常见的需求。当 Activity 因系统内存不足被销毁,或者因屏幕旋转等配置更改而重新创建时,需要恢复之前的状态,如用户的输入、选择等。onSaveInstanceStateonRestoreInstanceState 方法就是为此设计的,但它们的使用场景和持久化存储有所不同。

实现步骤

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);
}
}

最佳实践

  • 对于临时状态,如屏幕旋转时的状态,使用 onSaveInstanceStateonRestoreInstanceState
  • 对于持久化状态,如用户的设置、历史记录等,使用 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 来处理配置更改时,可能会导致一些意外问题,如屏幕显示异常。因此,建议优先使用 onSaveInstanceStateonRestoreInstanceState 来处理状态保存。


如何使用保存实例状态来保存活动状态
https://119291.xyz/posts/2025-05-12.how-to-save-activity-state-using-save-instance-state/
作者
ww
发布于
2025年5月12日
许可协议