使用片段保存状态

各种 Android 系统操作会影响片段的状态。为了确保用户状态被保存,Android 框架会自动保存和恢复片段以及后退栈。因此,您需要确保片段中的任何数据也被保存和恢复。

下表概述了导致片段丢失状态的操作,以及各种状态类型在这些更改中是否持久存在。表中提到的状态类型如下所示

  • 变量:片段中的局部变量。
  • 视图状态:片段中**一个或多个视图拥有**的任何数据。
  • 保存状态:此片段实例固有的数据,应保存在onSaveInstanceState()中。
  • 非配置:从外部来源(例如服务器或本地存储库)提取的数据,或用户创建的数据,这些数据在提交后会发送到服务器。

通常,变量保存状态的处理方式相同,但下表区分了这两者,以演示各种操作对每种操作的影响。

操作 变量 视图状态 保存状态 非配置
添加到后退栈 x
配置更改 x
进程死亡/重新创建 x ✓*
删除未添加到后退栈 x x x x
主机完成 x x x x

* 可以使用ViewModel 的保存状态模块在进程死亡期间保留非配置状态。

表 1:各种片段破坏性操作及其对不同状态类型的影响。

让我们看一个具体的例子。考虑一个生成随机字符串、在TextView中显示它并提供在发送给朋友之前编辑字符串选项的屏幕

random text generator app that demonstrates various
            types of state
图 1.演示各种状态类型的随机文本生成器应用。

对于此示例,假设一旦用户按下编辑按钮,应用就会显示一个EditText视图,用户可以在其中编辑消息。如果用户点击取消,则应清除EditText视图并将其可见性设置为View.GONE。这样的屏幕可能需要管理四部分数据以确保无缝体验

数据 类型 状态类型 描述
种子 长整型 非配置 用于随机生成新善举的种子。在创建ViewModel时生成。
随机善举 字符串 保存状态 + 变量 在第一次创建片段时生成。randomGoodDeed被保存以确保即使在进程死亡和重新创建后,用户也能看到相同的随机善举。
正在编辑 布尔值 保存状态 + 变量 当用户开始编辑时设置为true的布尔标志。isEditing被保存以确保在重新创建片段时编辑部分屏幕保持可见。
已编辑文本 可编辑 视图状态(由EditText拥有) EditText视图中已编辑的文本。EditText视图保存此文本以确保用户正在进行的更改不会丢失。

表 2:随机文本生成器应用必须管理的状态。

以下部分描述了如何在破坏性操作中正确管理数据的状态。

视图状态

视图负责管理自己的状态。例如,当视图接受用户输入时,视图有责任保存和恢复该输入以处理配置更改。所有 Android 框架提供的视图都有自己的onSaveInstanceState()onRestoreInstanceState()实现,因此您不必在片段中管理视图状态。

例如,在前面的场景中,已编辑的字符串保存在EditText中。EditText知道它正在显示的文本的值,以及其他详细信息,例如任何选定文本的开头和结尾。

视图需要一个 ID 来保留其状态。此 ID 必须在片段及其视图层次结构中是唯一的。没有 ID 的视图无法保留其状态。

<EditText
    android:id="@+id/good_deed_edit_text"
    android:layout_width="match_parent"
    android:layout_height="wrap_content" />

如表 1 所述,视图通过所有不会删除片段或销毁主机的操作保存和恢复其ViewState

保存状态

您的片段负责管理少量与片段功能息息相关的动态状态。您可以使用Fragment.onSaveInstanceState(Bundle)来保留易于序列化的数据。类似于Activity.onSaveInstanceState(Bundle),您放置在 bundle 中的数据会在配置更改和进程死亡及重建过程中保留,并且可以在您的片段的onCreate(Bundle)onCreateView(LayoutInflater, ViewGroup, Bundle)onViewCreated(View, Bundle)方法中使用。

继续前面的示例,randomGoodDeed是显示给用户的善举,而isEditing是一个标志,用于确定片段是显示还是隐藏EditText。此保存的状态应使用onSaveInstanceState(Bundle)持久化,如下例所示

Kotlin

override fun onSaveInstanceState(outState: Bundle) {
    super.onSaveInstanceState(outState)
    outState.putBoolean(IS_EDITING_KEY, isEditing)
    outState.putString(RANDOM_GOOD_DEED_KEY, randomGoodDeed)
}

Java

@Override
public void onSaveInstanceState(@NonNull Bundle outState) {
    super.onSaveInstanceState(outState);
    outState.putBoolean(IS_EDITING_KEY, isEditing);
    outState.putString(RANDOM_GOOD_DEED_KEY, randomGoodDeed);
}

要在onCreate(Bundle)中恢复状态,请从 bundle 中检索存储的值

Kotlin

override fun onCreate(savedInstanceState: Bundle?) {
    super.onCreate(savedInstanceState)
    isEditing = savedInstanceState?.getBoolean(IS_EDITING_KEY, false)
    randomGoodDeed = savedInstanceState?.getString(RANDOM_GOOD_DEED_KEY)
            ?: viewModel.generateRandomGoodDeed()
}

Java

@Override
public void onCreate(@Nullable Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    if (savedInstanceState != null) {
        isEditing = savedInstanceState.getBoolean(IS_EDITING_KEY, false);
        randomGoodDeed = savedInstanceState.getString(RANDOM_GOOD_DEED_KEY);
    } else {
        randomGoodDeed = viewModel.generateRandomGoodDeed();
    }
}

如表 1 所述,请注意,当片段放置在返回栈上时,变量将被保留。将它们视为保存的状态可确保它们在所有破坏性操作中都保持持久。

非配置

非配置数据应放置在片段之外,例如在ViewModel中。在上面的前一个示例中,seed(我们的非配置状态)是在ViewModel中生成的。维护其状态的逻辑由ViewModel拥有。

Kotlin

public class RandomGoodDeedViewModel : ViewModel() {
    private val seed = ... // Generate the seed

    private fun generateRandomGoodDeed(): String {
        val goodDeed = ... // Generate a random good deed using the seed
        return goodDeed
    }
}

Java

public class RandomGoodDeedViewModel extends ViewModel {
    private Long seed = ... // Generate the seed

    private String generateRandomGoodDeed() {
        String goodDeed = ... // Generate a random good deed using the seed
        return goodDeed;
    }
}

ViewModel类本质上允许数据在配置更改(例如屏幕旋转)中存活,并在片段放置在返回栈上时保留在内存中。在进程死亡和重建之后,ViewModel将被重新创建,并且会生成一个新的seed。将SavedState模块添加到您的ViewModel允许ViewModel在进程死亡和重建过程中保留简单状态。

其他资源

有关管理片段状态的更多信息,请参阅以下其他资源。

Codelabs

指南