ViewModel和onSaveInstanceState

目前可能很多人已经使用了jetpack,目前,我已经完全将kotlin+jetpack用于项目中。项目开发完毕,现在回头看看一些技术细节。

onSaveInstanceState()

onSaveInstanceState()UI组件停止运行、且未被主动销毁时调用。如用户点击Home键跳转到桌面、用户跳转到新的UI组件,该UI组件停止运行。
若是主动销毁,onSaveInstanceState()不会被调用。如用户主动点击返回按钮、代码调用finish()相关的函数。

系统可能因内存过低、电量过低时回收在后台运行的UI组件(Activity/Fragment),用户重新打开该UI组件时,会恢复在onSaveInstanceState()保存的数据。

onSaveInstanceState()适合保存少量数据。具体能保存多少数据,我并没有在任何地方看见官方说的具体值,文章Saving UI state with ViewModel SavedState and Dagger
的作者Nimrod Dayan介绍说限制在1MB以下,尽可能是能够恢复界面数据的最小数据集。

ViewModel

ViewModel能够在配置更改时保留,如当屏幕旋转时,UI组件重新构建,重新得到的仍然是同一个ViewModelViewModel为何能在UI组件销毁时存活,可以看文章每日一问 Activity 都重建了,你 Fragment凭什么活着?

ViewModel可以保存更多的数据在内存中,这些数据通常是需要在UI上显示的。在配置更改时,UI组件只是临时地销毁,系统会立刻生成新的UI组件,之前存在的ViewModel会立即通知新的UI组件更新界面,这样界面看起来就和配置更改前完全一样了。

总结

虽然在配置更改时,ViewModel并不会重新创建,但是在系统回收UI组件时,ViewModel同样会被销毁,此时唯一能保存数据的只有onSaveInstanceState()(当然还可以持久化数据到文件/网络等)。

可以看出ViewModelonSaveInstanceState()的功能都是保存数据,但是他们在某种层面上是完全不同的:

  1. onSaveInstanceState()适合保存能够恢复UI组件的最小数据集;ViewModel保存UI组件显示所需的所有数据和一些额外的数据。
  2. ViewModel能够在配置更改时保留数据;onSaveInstanceState()能够在用户临时离开UI组件时保留数据。若需要用户在长时间离开UI组件时保存数据,请使用数据持久化。
  3. ViewModelonSaveInstanceState()并不冲突,反而应该结合使用。onSaveInstanceState()保存了恢复UI组件的最小数据,ViewModel需要这些数据重新恢复UI组件需要的其他数据。

显然,ViewModel在保存数据方面,解决了onConfigurationChanged()的问题,并没有解决onSaveInstanceState()的问题。

ViewModel和onSaveInstanceState()结合使用

在没有ViewModel之前,我们会将加载数据的逻辑放在UI组件中。UI组件启动需要的最少数据通常会保留在启动时传入的数据集中,如Activity启动时会在Intent中存储对应的数据。

若下次启动该Activity需要的最少数据没有任何修改,那么我们可以重复利用Intent,因为系统回收UI组件后重启该组件,系统会保留同样的Intent。此时我们就可以不使用onSaveInstanceState()

但是,若在UI组件存活时,更改过该组件启动需要的最小数据集,那么就不能使用Intent了。我们就应该在onSaveInstanceState()中保存最新的数据。

现代的组件架构会将业务逻辑放在ViewModel中,所以重新启动UI组件的最小数据集需要被传递给ViewModelViewModel依赖这些数据来加载恢复UI组件的更多数据。此时,我们就需要结合onSaveInstanceState()ViewModel使用了。

流程:

  1. ActivityIntent获取数据,或从onCreate(Bundle)中得到onSaveInstanceState()保存的数据
  2. 将这些数据赋值给ViewModel
  3. ViewModel使用这些数据加载UI组件显示的数据

上面的流程虽然没有任何问题,但是ViewModel的行为是完全依赖IntentonSaveInstanceState()的数据,它并没有将ViewModel和这些数据进行强绑定。

我们期望在构造ViewModel时就将这些数据传递给ViewModel使用。此时我们应该使用ViewModelProvider.Factory

如我们进入一个文章详情页面:

class ArticleDetailViewModel(private val id: Long) : ViewModel() {
    class Factory(private val id: Long) : ViewModelProvider.Factory() {
        override fun create(modelClass: Class): T {
            return modelClass.getConstructor(Long::class.java).newInstance(id)
        }
    }

    private val _article = MutableLiveData
() val article: LiveData
get() = _article // ... } class ArticleDetailActivity: AppCompatActivity() { private val articleDetailViewModel: ArticleDetailViewModel by viewModels(factoryProducer = { ArticleDetailViewModel.Factory(intent.getLong("articleId", -1L)) } ) override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) setContentView(R.layout.activity_article_detail) articleDetailViewModel.article.observe(this) { article -> // show article content } // ... } }

这个文章详情页面依赖的最小数据集只有一个文章id。通过文章idViewModel加载文章的数据,UI组件观察到数据后进行显示。

你可能感兴趣的:(ViewModel和onSaveInstanceState)