ViewModel数据存储能力和原理

1,481 阅读3分钟

什么是viewmodel

  • 具备宿主生命周期感知能力的数据存储组件
  • viewmode保存的数据,在页面因为配置变更导致页面销毁重建之后依然存在,复用同一个viewmodel实例

viewmodel什么情况下能复用

  • 配置变更如:横竖屏切换,分辨率调整,权限变更,系统字体样式
  • 如果内存不足,电量不足等系统原因导致页面被回收(savedState的能力)

viewmodel的用法

用法一

这样的viewmodel是可以做到因为系统原因的页面销毁重建,做到数据的保存,实际上是对Viewmodel的复用

class mViewModel:ViewModel() {
    val getHttp : MutableLiveData<T>
    fun getHttp():LiveData<T>{
        // 如果因为配置变更而页面销毁,viewmodel实例被保存下来
        // 所以getHttp.value 有值,就可以不发起网络请求了
        if(getHttp.value == null){
            data = api.getHttp()
            getHttp.value = data
        }
        return getHttp
    }
}

class Activity {
    mViewModel.getHttp().observer(this,Observer{
        // 获取数据
    })
}


用法二

这个无论是配置不足还是内存,电量等原因导致页面被回收重建时

无论是不是同一个viewmodel实例都能做到复用

需要引入 api 'androidx.lifecycle:lifecycle-viewmodel-savedstate:2.2.0'

class mViewModel(val savedState:SavedStateHandle):ViewModel() {
    val getHttp = MutableLiveData<T>()
    
    // 一级页面
    fun getHttpData(){
       // 有数据就返回,内存级别的复用,都可以不是同一个viewmodel实例
       if(getHtt.value == null){
          getHttp.value = savedState.get<T>(Key) 
          return getHttp 
       }
       
       // 没有数据
       val data = api.getHttpData()
       // 保存数据
       savedState.set(Key,data)
       getHttp.value = data
       return getHttp
    }
}

用法三

跨页面数据共享

class MyApplication:Application(),viewModelStoreOwner {
   prvate val appViewModelStore :ViewModelStore by lazy{
       ViewModelStore()
   }
   
   override fun getViewModelStore(): ViewModelStore {
        return appViewModelStore
    }
}

// 创建 传applicaiton
val viewmodel = ViewProider(application).get(xxxViewModel::class.java)

viewmodel复用如何实现的?

我们从viewmodel的创建入手

ViewModelProvider(this).get(xxxViewModel::class.java)

先看到ViewModelProvider是接收一个ViewModelStoreOwnerFactory(系统帮我们创建的)

而我们的activity或者Fragment都是实现了ViewModelStoreOwner接口的 重写了getViewModelStore,返回一个ViewModelStore对象,所以可以直接传this

然后将我们传入的两个值ViewModelStoreFactory做了一个保存 image.png

而ViewModelStore本质上就是一个HashMap,用来存放我们的ViewModel image.png

再接着看get方法,是先从ViewModelStore去获取viewmodel,没有的话就用Factory去创建 image.png

既然ViewModel保存在ViewModelStore,那么只要ViewModelStore存在,viewmodel就存在

所以viewmodel实例的复用就变成ViewModelStore的复用了

ViewModelStore是在Activity中获取的,所以进入Activity#getViewModelStore()

可以看到NonConfigurationInstances中拿到ViewModelStore的 image.png

NonConfigurationInstances是用来因为配置变更时保存数据用的,它也保存了我们的ViewModelStore,那么是在哪里做了保存呢 image.png

你可以搜索一个NonConfigurationInstance哪里new出来的, 毕竟要保存就必须拿到NonConfigurationInstance.viewModelStore = xxx 去保存,所以我找到 onRetainNonConfigurationInstance方法中存了 image.png

而最终这个方法ActivityThread中调用到,涉及到activity销毁重建的流程,具体的流程你可以自己研究一下~

SavedState能力,viewmodel不是同一个实例都可以做到数据复用,怎么做到的?

几个重要类的类图 image.png

数据存储流程 image.png

数据恢复流程 image.png

数据复用流程 image.png

其实SavedState很简单,上面三张图就已经说全了,而且暂时也不知道怎么做解释,感觉代码截图出来都没什么好写的,如果日后有研究再写

为什么要使用viewmodel?意义在哪,为什么MVP也可以,为什么就要设计出vm层呢

  • 因为传统的MVP中,P层还是会有view层的应用,而且P层不知道V层是否被销毁了。涉及到了一个异步回调问题,但是MVVM模式中,V层会依赖VM层,而VM层的生命周期是与V层一致的,就算数据来了,VM层也能通过感知宿主的生命周期去派发数据,不会造成内存泄漏。而且也没有V层的引用。

  • 能做到Fragment之间的数据共享,比如主页中的Activity通常都有4个Tab,tab都是Fragment,如果Fragment之间要交付,免不了写一些回调,或者持有另一个Fragment的引用,耦合度很高。此时就可以让这4个Fragment用同一个ViewModel去操作了

  • 其他的我想不到了,你晓得的话,评论区告诉我吧