美文网首页
ViewModel和onSaveInstanceState

ViewModel和onSaveInstanceState

作者: MycroftWong | 来源:发表于2020-04-28 14:22 被阅读0次

    目前可能很多人已经使用了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>): T {
                return modelClass.getConstructor(Long::class.java).newInstance(id)
            }
        }
    
        private val _article = MutableLiveData<Article>()
    
        val article: LiveData<Article>
            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

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