各种 Android 系统操作可能会影响片段的状态。为了确保用户的状态被保存,Android 框架会自动保存和恢复片段和后退栈。因此,您需要确保片段中的任何数据也都被保存和恢复。
下表概述了导致片段丢失状态的操作,以及各种状态类型在这些更改中是否持续存在。表中提到的状态类型如下所示
- 变量:片段中的局部变量。
- 视图状态:片段中**一个或多个视图拥有**的任何数据。
- 保存状态:此片段实例固有的数据,应在
onSaveInstanceState()
中保存。 - 非配置:从外部来源(例如服务器或本地存储库)提取的数据,或提交后发送到服务器的用户创建的数据。
通常,变量与保存状态的处理方式相同,但下表区分了这两者,以演示各种操作对每种操作的影响。
操作 | 变量 | 视图状态 | 保存状态 | 非配置 |
---|---|---|---|---|
添加到后退栈 | ✓ | ✓ | x | ✓ |
配置更改 | x | ✓ | ✓ | ✓ |
进程死亡/重建 | x | ✓ | ✓ | ✓* |
已移除且未添加到后退栈 | x | x | x | x |
主机已完成 | x | x | x | x |
* 可以使用ViewModel 的保存状态模块在进程死亡期间保留非配置状态。
表 1:各种片段破坏性操作及其对不同状态类型的影响。
让我们看一个具体的例子。考虑一个生成随机字符串、在TextView
中显示它并提供在发送给朋友之前编辑字符串的选项的屏幕
对于此示例,假设一旦用户按下编辑按钮,应用就会显示一个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
- 生命周期感知组件codelab