Skip to content
Go back

Android 中旋转屏幕后如何保存 Activity 状态

在做 Android 页面时遇到过一个很容易忽略的问题:输入框里已经输入了一些内容,或者页面切换到了某个选项卡,一旋转屏幕,页面重新加载以后状态就没有了。

这个现象并不是系统把界面“刷新”了一次那么简单。默认情况下,屏幕方向变化属于配置改变,当前 Activity 会被销毁并重新创建。如果页面状态只保存在成员变量中,新的实例自然无法知道旧页面发生过什么。

先观察生命周期

可以在生命周期方法中打印日志:

@Overrideprotected void onCreate(Bundle savedInstanceState) {    super.onCreate(savedInstanceState);    Log.d("StateDemo", "onCreate");}@Overrideprotected void onDestroy() {    super.onDestroy();    Log.d("StateDemo", "onDestroy");}

旋转屏幕以后会看到旧页面走过 onDestroy(),紧接着新的页面又走到 onCreate()。所以问题的本质是:如何把重建前的重要状态传给新的页面实例。

使用 onSaveInstanceState 保存临时界面状态

对于当前选中的 tab、列表滚动位置、尚未提交的筛选条件这类临时界面状态,可以通过 onSaveInstanceState() 保存:

private static final String KEY_SELECTED_TAB = "selected_tab";private static final String KEY_KEYWORD = "keyword";private int mSelectedTab = 0;@Overrideprotected void onSaveInstanceState(Bundle outState) {    outState.putInt(KEY_SELECTED_TAB, mSelectedTab);    outState.putString(KEY_KEYWORD, mSearchEditText.getText().toString());    super.onSaveInstanceState(outState);}

onCreate() 中判断 savedInstanceState 是否为空,再将状态恢复回来:

@Overrideprotected void onCreate(Bundle savedInstanceState) {    super.onCreate(savedInstanceState);    setContentView(R.layout.activity_main);    mSearchEditText = (EditText) findViewById(R.id.search_edit_text);    if (savedInstanceState != null) {        mSelectedTab = savedInstanceState.getInt(KEY_SELECTED_TAB, 0);        String keyword = savedInstanceState.getString(KEY_KEYWORD, "");        mSearchEditText.setText(keyword);        showTab(mSelectedTab);    }}

部分系统控件,例如设置了 idEditText,系统本身能够恢复一定状态。但如果页面状态会影响业务展示,最好仍然明确知道哪些字段需要保存,而不是完全依赖默认行为。

Bundle 适合保存什么

Bundle 适合保存数量不大、能够快速序列化的页面状态,例如:

它并不适合保存大图、完整列表数据或大量网络返回内容。把这些对象都塞进状态中,会让重建过程变得沉重,也可能触发 TransactionTooLargeException 一类问题。

更合适的做法是保存能够重新找到数据的关键标识,例如一条内容的 id,页面重新创建后再从缓存、数据库或网络加载详情。

状态与数据不是一回事

这次问题让我比较明确地意识到,页面上看起来都属于“内容”的东西,实际上可以分成两类:

界面状态适合在页面重建时保存与恢复;业务数据则应该有自己的来源和缓存方式。把这两类东西混在一起,页面稍微复杂以后就很难维护。

是否禁止 Activity 重建

也可以在配置中自行处理屏幕方向变化,避免系统重新创建 Activity。但这样只是把问题转移到了自己手上:资源切换、尺寸变化和布局更新都需要正确处理。

对于普通页面,理解并正确处理状态恢复通常更加可靠,也更符合系统的工作方式。

小结

屏幕旋转导致内容丢失,表面上是一个小问题,背后却是在提醒我们:一个页面并不是永远存在的实例。

在 Activity 可能重建的前提下,明确哪些是短暂的界面状态、哪些是需要重新获取的数据,再使用 onSaveInstanceState() 保存必要信息,页面行为才会更加稳定。


Share this post on: