Android ViewModel

98 阅读2分钟

ViewModel

  • 旨在以注重生命周期的方式存储和管理界面相关的数据。ViewModel 类让数据可在发生屏幕旋转等配置更改后继续留存,负责为界面准备数据。在配置更改期间会自动保留 ViewModel 对象,以便它们存储的数据立即可供下一个 Activity 或 Fragment 实例使用。

ViewModel可以解决什么问题

1.数据保存

如果系统销毁或重新创建界面控制器,则存储在其中的任何瞬态界面相关数据都会丢失,为了避免我们的程序出现上述这种情况,我们除了使用Activity的savedInstanceState(仅适合可以序列化再反序列化的少量数据)保存数据之外,还可以使用ViewModel来进行处理数据(可保存较大数据)

存储在Activity的变量

class TestViewModelActivity :FragmentActivity(R.layout.activity_view_model){

    var activityValue: String = "在Activity的值-初始化"
}

存储在ViewModel的变量

class TestActivityViewModel(private val savedStateHandle: SavedStateHandle) : ViewModel() {

    var viewModelValue: String = "在ViewModel的值-初始化"
    var viewModelSaveValue: String by Delegates.observable("在ViewModel手动保存的值-初始化"){ _, old, new->
        saveS2(new)
    }

    init {
        readS2()
    }

    private fun readS2() {
        val value: Any? = savedStateHandle["s2"]
        if (value is String) {
            viewModelSaveValue = value
        }
    }

    private fun saveS2(s: String) {
        savedStateHandle["s2"] = s
    }
}

image-20221012114026048.png

更改值

btn1.setOnClickListener {
    mViewModel.viewModelValue="在Activity的值-更改后的值"
    mViewModel.viewModelSaveValue="在ViewModel的值-更改后的值"
    activityValue="在ViewModel手动保存的值-更改后的值"
    tv1.text=activityValue+"---"+mViewModel.viewModelValue+"---"+mViewModel.viewModelSaveValue
}

image-20221012114110535.png

旋转屏幕后 保存在Activity的值重新初始化了

image-20221012114231588.png

Activity被回收后,保存在ViewModel的值也重新初始化了,但是手动保存的可以再读出来

image-20221012114653396.png

注:SavedStateHandle不需要我们手动创建,Activity默认的ViewModelProviderFactory是SavedStateViewModelFactory,会自动帮我们创建SavedStateHandle

if (isAndroidViewModel && mApplication != null) {
    viewmodel = constructor.newInstance(mApplication, controller.getHandle());
} else {
    viewmodel = constructor.newInstance(controller.getHandle());
}

2.简化资源管理工作,避免内存泄漏风险

  • Activity 和 Fragment经常需要进行可能需要一些时间才能返回的异步调用(如网络请求),我们需要确保系统在其销毁后清理这些调用以避免潜在的内存泄漏,同时如果配置发生更改重新创建对象的时候可能会发生重复此前已完成的工作,造成资源浪费

  • 让 Activity 和 Fragment 专注于界面显示,如果要求界面控制器也负责从数据库或网络加载数据,那么会使类越发膨胀,ViewModel可以更容易、高效的分离出视图数据操作

    如下面登录方法里,使用viewModelScope启动协程,如果在LoginViewModel被销毁了协程还没有执行结束,viewModelScope启动的协程将被取消

    class LoginViewModel (val userRepository: UserRepository) : ViewModel() {
    
        fun login(account: String, password: String, rememberPassword: Boolean) {
            viewModelScope.launch {
                //登录
                val resource = userRepository.login(account, password)
                  //更新界面...
            }
        }
    }
    

3.实现Fragment和Activity/Fragment共享数据

class TestViewModelActivity :FragmentActivity(R.layout.activity_view_model){
  //Activity中的实例
  private val mViewModel: TestActivityViewModel by viewModels()
}

class ViewModelFragment1:Fragment(R.layout.fragment_veiw_model1) {
  //Fragment中使用viewModels将创建新的实例
  private val viewModel:TestActivityViewModel by viewModels()
  //Fragment中使用activityViewModels的实例和Activity中的为同一实例
  private val shareViewModel:TestActivityViewModel by activityViewModels()
}