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