
本文介绍如何使用 onSaveInstanceState() 和 Bundle 在 Activity 重建(如屏幕旋转)时持久化 9 个按钮的文本状态(X/O),避免数据丢失,无需依赖 SharedPreferences 或 ViewModel。
本文介绍如何使用 onsaveinstancestate() 和 bundle 在 activity 重建(如屏幕旋转)时持久化 9 个按钮的文本状态(x/o),避免数据丢失,无需依赖 sharedpreferences 或 viewmodel。
在开发井字棋(Tic-Tac-Toe)类游戏时,常使用 Button[] buttons = new Button[9] 表示棋盘格。用户点击后按钮显示 "X" 或 "O",但 Android 在屏幕旋转时会销毁并重建 Activity —— 若不主动保存状态,所有按钮文本将重置为空,导致游戏进度丢失。
核心思路:将 9 个按钮的当前文本状态序列化为轻量字符串(如 "X,-,O,X,O,-,-,X,O"),在 onSaveInstanceState() 中存入 Bundle,并在 onCreate() 中解析还原。
✅ 正确实现步骤
1. 保存按钮状态(onSaveInstanceState)
在按钮状态更新后(例如点击逻辑执行完毕),将所有按钮文本拼接为统一格式的字符串:
@Override
protected void onSaveInstanceState(@NonNull Bundle outState) {
super.onSaveInstanceState(outState);
StringBuilder stateBuilder = new StringBuilder();
for (int i = 0; i < buttons.length; i++) {
String text = buttons[i].getText().toString().trim();
// 用 "-" 表示空按钮,确保长度固定且可解析
stateBuilder.append(text.isEmpty() ? "-" : text);
if (i < buttons.length - 1) stateBuilder.append(",");
}
outState.putString("board_state", stateBuilder.toString());
}? 提示:使用
"-"占位空按钮,比null或空字符串更安全,避免解析歧义;逗号分隔便于后续split()拆分。
2. 恢复按钮状态(onCreate)
在 onCreate() 中检查 savedInstanceState 是否存在,并逐项还原:
@Override
public void onCreate(@Nullable Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
// 初始化 buttons 数组(通过 findViewById 或 ViewBinding)
initButtons(); // 请确保此方法已正确绑定 9 个 Button 实例
// 尝试恢复状态
if (savedInstanceState != null) {
String savedState = savedInstanceState.getString("board_state");
if (savedState != null) {
String[] states = savedState.split(",");
for (int i = 0; i < Math.min(states.length, buttons.length); i++) {
String value = states[i].trim();
buttons[i].setText(value.equals("-") ? "" : value);
// 可选:同步设置颜色(根据 X/O 匹配原逻辑)
if ("X".equals(value)) {
buttons[i].setTextColor(Color.parseColor("#ff4a4a"));
} else if ("O".equals(value)) {
buttons[i].setTextColor(Color.parseColor("#70FFEA"));
}
}
}
}
}3. 优化点击逻辑(保持状态一致性)
你原有的 onClick 方法已合理控制逻辑,只需确保状态变更后 不依赖外部变量(如 gameState[])单独维护,而是以按钮自身 text 为准 —— 这样 onSaveInstanceState 读取的才是真实 UI 状态。
✅ 推荐补充:
- 在
onClick结束前调用invalidate()或确保 UI 立即刷新; - 避免在
onSaveInstanceState中访问可能为null的buttons[i](建议在initButtons()中完成非空校验); - 如需支持深色模式或字体缩放,可额外保存
textColor,但对本场景非必需。
⚠️ 注意事项
- ❌ 不要使用
SharedPreferences存储瞬时 UI 状态:它面向跨进程/跨生命周期持久化,有 I/O 开销,且不适用于旋转这种高频、临时性重建; - ❌ 不要在
onDestroy()中保存状态:该方法不保证被调用(如系统杀进程),而onSaveInstanceState()是专为此类场景设计的标准生命周期回调; - ✅
Bundle限制约 1MB,9 个字符 + 分隔符远低于阈值,完全安全; - ? 若后续引入
ViewModel,可将状态提升至ViewModel层,但对纯旋转恢复,Bundle方案更简洁、零依赖。
通过以上方式,你的井字棋界面即可在任意方向旋转后完美保留每一步落子,用户体验无缝连贯。

















