ViewModel 的已保存状态模块(视图) Android Jetpack 的一部分。
保存界面状态这篇文章提到过,ViewModel 对象可以处理
配置更改,因此您无需担心旋转时
或其他情况下的状态。但是,如果需要处理系统发起的进程终止,则可以使用 SavedStateHandle API 作为备用方式。
界面状态通常在 ViewModel 对象中(而不是
activity 中)存储或引用,因此,使用 onSaveInstanceState() 时需要已保存的状态模块
可以为您处理的某个样板文件。
使用此模块时,ViewModel 对象会通过其构造函数接收 SavedStateHandle
对象。此对象是一个键值对映射,用于向已保存状态写入对象以及从其中检索对象。这些值会在进程被系统终止后继续保留,并通过同一对象保持可用状态。
保存的状态与您的任务堆栈相关联。如果任务堆栈消失,保存的状态也会消失。强制停止应用、从“最近用过”菜单中移除应用或重新启动设备时,可能会发生这种情况。在这种情况下,任务堆栈会消失,并且您无法恢复保存的状态中的信息。在 用户发起的界面状态解除情景中,不会恢复保存的状态。在 系统发起的情景中,会恢复保存的状态。
设置
从 Fragment 1.2.0 或其传递依赖项
Activity 1.1.0 开始,您可以接受 SavedStateHandle 作为构造函数
参数,用于您的 ViewModel。
Kotlin
class SavedStateViewModel(private val state: SavedStateHandle) : ViewModel() { ... }
Java
public class SavedStateViewModel extends ViewModel { private SavedStateHandle state; public SavedStateViewModel(SavedStateHandle savedStateHandle) { state = savedStateHandle; } ... }
然后,您可以检索 ViewModel 的实例,而无需任何其他配置。默认 ViewModel 工厂会向您的 ViewModel 提供相应的 SavedStateHandle。
Kotlin
class MainFragment : Fragment() { val vm: SavedStateViewModel by viewModels() ... }
Java
class MainFragment extends Fragment { private SavedStateViewModel vm; public void onViewCreated(@NonNull View view, Bundle savedInstanceState) { vm = new ViewModelProvider(this).get(SavedStateViewModel.class); ... } ... }
提供自定义 ViewModelProvider.Factory 实例时,您可以通过扩展
AbstractSavedStateViewModelFactory 来启用 SavedStateHandle 的使用。
使用 SavedStateHandle
SavedStateHandle 类是一个键值对映射,用于向已保存的状态写入数据以及从中
检索数据,可通过 set() 和
get() 方法实现。
通过使用 SavedStateHandle,查询值会在进程终止后保留下来,从而确保用户在重新创建前后看到同一组过滤后的数据,而无需 activity 或 fragment 手动保存、恢复该值并将其重新转给 ViewModel。
此外,SavedStateHandle 还包含其他您与键值对映射互动时可能会用到的方法:
contains(String key)- 检查给定键是否存在值。remove(String key)- 移除给定键的值。keys()- 返回SavedStateHandle中包含的所有键。
此外,您还可以使用可观测数据存储器从 SavedStateHandle 检索值。支持的类型包括:
LiveData
从封装在 LiveData
可观察对象中的 SavedStateHandle 检索值,使用 getLiveData()。当键的值更新时,LiveData
会收到新值。通常,该值是因用户互动而设置的,例如输入查询以过滤一系列数据。然后,您可以使用以下更新后的
值转换 transform LiveData。
Kotlin
class SavedStateViewModel(private val savedStateHandle: SavedStateHandle) : ViewModel() { val filteredData: LiveData<List<String>> = savedStateHandle.getLiveData<String>("query").switchMap { query -> repository.getFilteredData(query) } fun setQuery(query: String) { savedStateHandle["query"] = query } }
Java
public class SavedStateViewModel extends ViewModel { private SavedStateHandle savedStateHandle; public LiveData<List<String>> filteredData; public SavedStateViewModel(SavedStateHandle savedStateHandle) { this.savedStateHandle = savedStateHandle; LiveData<String> queryLiveData = savedStateHandle.getLiveData("query"); filteredData = Transformations.switchMap(queryLiveData, query -> { return repository.getFilteredData(query); }); } public void setQuery(String query) { savedStateHandle.set("query", query); } }
支持的类型
保留在 SavedStateHandle 中的数据将作为 Bundle 与 activity 或
fragment 的 savedInstanceState 的其余部分一起保存和恢复。
保存非 Parcelable 类
如果某个类未实现 Parcelable 或 Serializable 且不能修改为实现这些接口之一,则无法直接将该类的实例保存到 SavedStateHandle 中。
从 Lifecycle 2.3.0-alpha03 开始,SavedStateHandle 允许您保存
任何对象,具体方法是:使用 setSavedStateProvider() 方法提供您自己的逻辑用于将对象作为
Bundle 来保存和恢复。
SavedStateRegistry.SavedStateProvider 是一个接口,用于定义一个
单个 saveState() 方法来返回包含您希望保存的状态的 Bundle。当 SavedStateHandle
准备好保存其状态后,它会调用 saveState() 以从 SavedStateProvider 检索 Bundle,并为关联的键保存
Bundle。
设想一个示例应用,该应用通过
ACTION_IMAGE_CAPTURE Intent 向相机应用请求图片,并传递一个临时文件,以供
相机存储图片。TempFileViewModel 封装了创建该临时文件的逻辑。
Kotlin
class TempFileViewModel : ViewModel() { private var tempFile: File? = null fun createOrGetTempFile(): File { return tempFile ?: File.createTempFile("temp", null).also { tempFile = it } } }
Java
class TempFileViewModel extends ViewModel { private File tempFile = null; public TempFileViewModel() { } @NonNull public File createOrGetTempFile() { if (tempFile == null) { tempFile = File.createTempFile("temp", null); } return tempFile; } }
为确保临时文件在 activity 的进程终止随后又恢复后不会丢失,TempFileViewModel 可以使用 SavedStateHandle 保留其数据。如需允许 TempFileViewModel 保存其数据,请实现 SavedStateProvider,并在 ViewModel 的 SavedStateHandle 上将其设置为提供程序:
Kotlin
private fun File.saveTempFile() = bundleOf("path", absolutePath) class TempFileViewModel(savedStateHandle: SavedStateHandle) : ViewModel() { private var tempFile: File? = null init { savedStateHandle.setSavedStateProvider("temp_file") { // saveState() if (tempFile != null) { tempFile.saveTempFile() } else { Bundle() } } } fun createOrGetTempFile(): File { return tempFile ?: File.createTempFile("temp", null).also { tempFile = it } } }
Java
class TempFileViewModel extends ViewModel { private File tempFile = null; public TempFileViewModel(SavedStateHandle savedStateHandle) { savedStateHandle.setSavedStateProvider("temp_file", new TempFileSavedStateProvider()); } @NonNull public File createOrGetTempFile() { if (tempFile == null) { tempFile = File.createTempFile("temp", null); } return tempFile; } private class TempFileSavedStateProvider implements SavedStateRegistry.SavedStateProvider { @NonNull @Override public Bundle saveState() { Bundle bundle = new Bundle(); if (tempFile != null) { bundle.putString("path", tempFile.getAbsolutePath()); } return bundle; } } }
如需在用户返回时恢复 File 数据,请从 SavedStateHandle 中检索 temp_file Bundle。这正是 saveTempFile() 提供的包含绝对路径的 Bundle。该绝对路径随后可用于实例化新的 File。
Kotlin
private fun File.saveTempFile() = bundleOf("path", absolutePath) private fun Bundle.restoreTempFile() = if (containsKey("path")) { File(getString("path")) } else { null } class TempFileViewModel(savedStateHandle: SavedStateHandle) : ViewModel() { private var tempFile: File? = null init { val tempFileBundle = savedStateHandle.get<Bundle>("temp_file") if (tempFileBundle != null) { tempFile = tempFileBundle.restoreTempFile() } savedStateHandle.setSavedStateProvider("temp_file") { // saveState() if (tempFile != null) { tempFile.saveTempFile() } else { Bundle() } } } fun createOrGetTempFile(): File { return tempFile ?: File.createTempFile("temp", null).also { tempFile = it } } }
Java
class TempFileViewModel extends ViewModel { private File tempFile = null; public TempFileViewModel(SavedStateHandle savedStateHandle) { Bundle tempFileBundle = savedStateHandle.get("temp_file"); if (tempFileBundle != null) { tempFile = TempFileSavedStateProvider.restoreTempFile(tempFileBundle); } savedStateHandle.setSavedStateProvider("temp_file", new TempFileSavedStateProvider()); } @NonNull public File createOrGetTempFile() { if (tempFile == null) { tempFile = File.createTempFile("temp", null); } return tempFile; } private class TempFileSavedStateProvider implements SavedStateRegistry.SavedStateProvider { @NonNull @Override public Bundle saveState() { Bundle bundle = new Bundle(); if (tempFile != null) { bundle.putString("path", tempFile.getAbsolutePath()); } return bundle; } @Nullable private static File restoreTempFile(Bundle bundle) { if (bundle.containsKey("path") { return File(bundle.getString("path")); } return null; } } }
为你推荐
- 注意:当 JavaScript 处于关闭状态时,系统会显示链接文字
- 保存界面状态
- 使用可观察的数据对象
- 创建具有依赖项的 ViewModel