Android无需SharedPreference实现按返回键Activity数据不丢失
Activity按返回键回退时页面数据丢失解决方案
不需要使用SharedPreference,完全可以实现返回时保留Activity1的原有数据。
正常情况下,从Activity1启动Activity2后,Activity1会被压入返回栈进入停止状态,按设备返回键时系统会将栈内留存的Activity1实例恢复到前台,数据本应保留。出现数据丢失基本是三类原因:
- Activity1在后台时因系统内存不足被回收,返回时重建了新的Activity1实例
- 代码逻辑存在问题:比如在onResume等生命周期里无条件重置页面数据,或是启动Activity时加了清空栈的Flag
- 启动Activity用了错误的Context,导致系统栈管理异常,原有Activity1实例被销毁
方案1:使用原生瞬时状态保存API(无额外依赖,兼容所有Android版本)
这是Android官方提供的专门用于存储页面临时状态的能力,不需要做任何持久化写入。
- 在Activity1中重写
onSaveInstanceState方法,在系统回收Activity前把需要保留的页面数据存入Bundle:
@Override protected void onSaveInstanceState(@NonNull Bundle outState) { super.onSaveInstanceState(outState); // 按需存入所有需要保留的页面数据,比如输入框内容、列表数据、选中状态等 outState.putString("et_input", editText.getText().toString()); outState.putInt("selected_tab", currentTabPos); outState.putParcelableArrayList("list_data", dataList); }
- 在Activity1的
onCreate方法中判断是否有已保存的状态,有则直接恢复数据,没有则走首次初始化逻辑:
@Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_first); // 先完成控件初始化 editText = findViewById(R.id.et_input); recyclerView = findViewById(R.id.rv_list); if (savedInstanceState != null) { // 取出之前保存的数据恢复页面 String savedInput = savedInstanceState.getString("et_input"); int savedTab = savedInstanceState.getInt("selected_tab"); ArrayList<MyData> savedList = savedInstanceState.getParcelableArrayList("list_data"); editText.setText(savedInput); dataList.clear(); if (savedList != null) dataList.addAll(savedList); adapter.setSelectedPos(savedTab); adapter.notifyDataSetChanged(); } else { // 首次进入页面的正常初始化逻辑 loadDefaultData(); } }
方案2:使用ViewModel持有页面数据(适配Jetpack架构,代码更简洁)
如果项目接入了Jetpack组件,优先用ViewModel存储页面数据。ViewModel的生命周期独立于Activity实例,只要Activity1还在返回栈中,ViewModel持有的数据就不会被销毁,不需要手动写Bundle存取逻辑。
- 定义对应Activity1的ViewModel类,所有页面状态数据都存在ViewModel中:
public class FirstViewModel extends ViewModel { public MutableLiveData<String> inputText = new MutableLiveData<>(""); public MutableLiveData<List<MyData>> pageList = new MutableLiveData<>(new ArrayList<>()); public int currentTab = 0; }
- 在Activity1中获取ViewModel实例,所有数据操作都针对ViewModel进行,UI观察ViewModel的数据变化自动更新:
private FirstViewModel viewModel; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_first); viewModel = new ViewModelProvider(this).get(FirstViewModel.class); // 初始化控件 editText = findViewById(R.id.et_input); recyclerView = findViewById(R.id.rv_list); // 观察数据变化更新UI viewModel.inputText.observe(this, text -> editText.setText(text)); viewModel.pageList.observe(this, list -> adapter.updateData(list)); // 用户操作时直接更新ViewModel中的数据 editText.addTextChangedListener(new TextWatcher() { @Override public void beforeTextChanged(CharSequence s, int start, int count, int after) {} @Override public void onTextChanged(CharSequence s, int start, int before, int count) { viewModel.inputText.setValue(s.toString()); } @Override public void afterTextChanged(Editable s) {} }); }
如果需要兼容系统在后台杀死进程的场景,可以给ViewModel配合SavedStateHandle使用,无需额外写onSaveInstanceState逻辑即可自动保存恢复状态。
必须修正的现有代码问题
你当前的启动代码存在两个容易引发栈异常的问题,必须修改:
- 启动Activity不要用Application Context,换成当前Activity的Context,避免栈管理异常导致Activity1被意外销毁
- 类名遵循Java大驼峰命名规范,不要用全小写的类名,避免混淆或编译异常
修改后的启动代码如下:
// 把secondactivity改成大驼峰命名的SecondActivity Intent intent = new Intent(FirstActivity.this, SecondActivity.class); startActivity(intent);
额外检查项:
- 不要在Activity1的
onResume方法里无条件重置页面数据、重新加载初始数据,否则每次回到前台数据都会被覆盖 - 启动Activity2的Intent不要加
FLAG_ACTIVITY_CLEAR_TOP、FLAG_ACTIVITY_NO_HISTORY、FLAG_ACTIVITY_CLEAR_TASK这类会清空Activity栈的Flag,否则返回时会创建新的Activity1实例,原有数据自然丢失
内容的提问来源于stack exchange,提问作者Brimo
相关产品推荐
相关产品推荐

