美文网首页
Activity & Fragment 的重建、数据恢复相关问题

Activity & Fragment 的重建、数据恢复相关问题

作者: 你可记得叫安可 | 来源:发表于2020-09-19 10:09 被阅读0次

    0. onSaveInstanceState & onRestoreInstanceState 的时机

    我们经常在网上看到说,onSaveInstanceStateActivity 退回到后台,且未来可能被系统杀死时,我们可以在 onSaveInstanceState 中保存一些临时数据,以便在系统真的杀死了进程,并回收了 Activity 后,用户回到 Activity 时,开发者能够在重建的 ActivityonCreateonRestoreInstanceState 方法中,能够从 Bundle 中恢复数据。

    1. onCreate 和 onRestoreInstanceState 两个回调中都带有恢复数据用的 Bundle 参数,为什么两个地方都可以恢复?

    这两个回调的时机不一样。

    • onCreate 是在系统构造 Activity 时被调用的。onCreate 中的 Bundle 如果为空,则表明这是一个新的 Activity 实例。onCreate 中的 Bundle 如果不为空,则表明这是一个重建的 Activity 实例。
    • onRestoreInstanceState 的调用是在 onStart 之后,而且只会在 Activity 被重建时且 Bundle 不为空时被调用。

    2. 那些情况下会被重建?

    我们知道在屏幕旋转时 ActivityFragment 会重建,其实还有一种情况会重建,就是我们时常看到博客里说的,当应用在后台时,进程被系统回收,用户再次回到应用时,应用会被重建。

    那么应用什么时候会被系统回收呢?我们需要清楚一点,就是系统不会单独地回收 Activity 或者 Fragment,而是会在系统资源不足时,根据应用所在的进程的状态来杀死进程,以回收资源。这里涉及到了一些进程状态的概念:前台进程、可见进程、服务进程 和 缓存进程(process lifecycle)。一般缓存进程会最先被系统回收。

    现在的手机 RAM 都非常大,我们怎么模拟这个系统回收进程的过程呢?可以到 开发者选项 -> 应用 -> 后台进程限制,将其从默认的 标准限制 改为 不得超过 1 个进程。这样我们就能够观察到 ActivityFragment 重建时,Bundle 中带有之前调用 onSaveInstanceState 时保存的值了。

    3. Fragment 重建时的回调为什么没有 onRestoreInstanceState

    Fragement 的数据恢复提供了另外两个回调,onCreateonCreateView,方便开发者在不同的时机恢复数据。

    4. Fragment.setRetainInstance 是如何保持 Fragment 的?

    FragmentretainInstance 属性默认为 false,当其设置为 true 时,表示 Fragment 实例会在 Activity 因配置变化而重建时,Fragment 自身实例会被保持,不会创建新的实例。它的原理是,调用该方法时,最终调用到了 FragmentManager.addRetainedFragment -> FragmentManagerViewModel.addRetainedFragmentFragment 实例保存到了 FragmentManagerViewModel.mRetainedFragments 中。mRetainedFragment 是一个以 Fragmentuuid(Fragment 自己生成)key,以 Fragment 自身实例为 valueHashMap。因此本质上是 FragmentManagerViewModel 是一个 ViewModel,它可以在重建周期内保持实例。

    retainInstance 设置为 true 之后,Fragment 的生命周期会有所变化。由于会保存实例,因此重建时不会再调用 onDestroy 销毁,也不会再调用 onCreate 重新实例化。但是 onDetachonAttach 依然会调。

    5. 什么时候用 onSaveInstanceState,什么时候用 ViewModel,什么时候用 Fragment.setRetainInstance?

    • onSaveInstanceState 能够在两种情况下恢复信息:1. 系统配置变化,2. 应用进程被系统回收。由于 onSaveInstanceState 使用 Bundle 来将数据序列化到磁盘,因此我们在使用 onSaveInstanceState 时应当注意:不要存储大量数据,只能存储简单的数据结构及基本类型。因此我们只能存储一些必要的数据,比如用户 ID,当我们重建 Activity 时,应用可以根据恢复的用户 ID 再次去网络请求 或 查询本地数据库,来获取更多信息以恢复页面。
    • ViewModel 保存的数据是在内存中,可以跨越系统配置变化,但是不能在应用进程被系统回收时依然保持数据。ViewModel 可以保持更复杂的数据结构。(似乎最近又出了一个 SavedStateHandle
    • ViewModel 完全可以替代 Fragment.setRetainInstance。事实上,ViewModel 的内部实现就调用了 Fragment.setRetainInstance

    正确的使用姿势应该是,onSaveInstanceStateViewModel 结合使用。

    • 当系统回收应用进程后,onSaveInstanceState 中的 Bundle 不为空,开发者应当将 Bundle 传给 ViewModelViewModel 发现自己缓存的数据为空,因此使用 Bundle 中的数据来加载页面。
    • 当应用配置改变而重建 Activity 时,onSaveInstanceState 中的 Bundle 不为空,开发者应当将 Bundle 传给 ViewModel。由于 ViewModel 自己有缓存的数据,因此最后由 ViewModel 自己决定使用缓存的数据还是 Bundle 中的数据。

    5. 怎么样可以使 Activity & Fragment 不再重建?

    • 可以通过在 AndroidManifest.xml 中配置 <activity>android:screenOrientation 属性,将 Activity 的方向固定,可以避免因屏幕旋转导致的重建,同时也不会回调 onConfigurationChanged。但是该属性在 多窗口系统 下会失效。
    • 通过配置 android:configChanges 可以控制在哪些系统配置改变的情况下 Activity 不重建。最常用的包括 orientationscreenSizekeyboardHidden。不过通过该方法,Activity 虽然不再重建,但是系统会回调 onConfigurationChanged,需要开发者自己处理配置的变换。

    6. onRetainCustomNonConfigurationInstance & onRetainNonConfigurationInstance 的作用

    我们知道 onSaveInstanceState 中保存的是能够被序列化的数据,Android 系统同样为我们提供了在配置改变时保存没有必要序列化的数据的方法:onRetainCustomNonConfigurationInstanceonRetainNonConfigurationInstance,这两个回调方法都返回一个 Object,区别在于,onRetainCustomNonConfigurationInstance 是开放给开发者来保存数据的时机的回调,onRetainNonConfigurationInstancefinal 方法,用于系统自己保存一些系统资源时使用。
    对于 onRetainCustomNonConfigurationInstance 保存的数据,之后我们在重建的 ActivityonCreate 方法中,可以通过 getLastCustomNonConfigurationInstance 来直接获得之前保存的数据。这个回调在 Androidx 中已经被标记为 Deprecated,这是因为该机制的职责已经由 ViewModel 代替了。
    对于 onRetainNonConfigurationInstance 保存的数据,其实通过阅读源码我们是可以发现,目前的实现就是用来保存了 ViewModel。而 ViewModel 之所以能够在系统配置改变后重建,正是使用了 onRetainNonConfigurationInstance 的恢复机制。

    7. ViewModel 如何解决重建问题的?

    相关文章

      网友评论

          本文标题:Activity & Fragment 的重建、数据恢复相关问题

          本文链接:https://www.haomeiwen.com/subject/tkleyktx.html