使用片段保存状态

各种 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 字符串 保存状态 + 变量 在片段第一次创建时生成。保存randomGoodDeed以确保即使在进程死亡和重新创建后,用户也能看到相同的随机善举。
isEditing 布尔值 保存状态 + 变量 当用户开始编辑时设置为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 中所述,请注意,当片段放置在返回堆栈上时,变量会保留。将它们视为保存状态可确保它们在所有破坏性操作中持久化。

非配置

NonConfig 数据应放置在片段之外,例如在ViewModel中。在上面的先前示例中,seed(我们的 NonConfig 状态)是在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

指南