ViewModel

视频链接:https://www.bilibili.com/video/av29949898/?spm_id_from=333.788.videocard.1

背景:为了帮助开发者更高效、更容易地构建优秀的应用,Google推出了Android Jetpack, 它包含了开发库、工具、以及最佳实践指南。其中的Lifecycle库可以有效避免内存泄漏和解决常见的Android生命周期难题。Lifecycle 库2.0版本还新添加了与Data Binding的集成,ViewModel类就属于Lifecycle库。

ViewModel是用来保存应用UI数据的类,并且它会在配置变更(Configuration Change)后继续存在。让我们来看看它的特点吧。

手机屏幕旋转是Configuration Change的一种典型场景。当旋转屏幕时,Activity会被重新创建,如果数据没有被正确的保存和恢复,就有可能丢失,从而导致莫名其妙的UI错误,甚至应用崩溃。相反的,ViewModel会在Configuration Change后继续存在。


如下的架构设计是更被推荐的:将所有的UI数据保存在ViewModel中,而不是Activity中。这样就能确保数据不会受到Configuration Change的影响。


将Activity UI 数据存储在ViewModel

Android开发时一个常见的坑是把很多变量、逻辑和数据摆在Activity或Fragment中,这样的代码比较混乱和难以维护。随着业务的发展和变更,可能会导致Activity越来越膨胀和臃肿。这种开发模式违反了「单一责任原则」。

Activity负责了太多的事情


ViewMode可以有效的划分责任,它可以用来保存Activity的所有UI数据,然后,Activity仅负责了解如何在屏幕上显示该数据和接受用户互动,但是它不会处理这些互动。如果你的应用加载和存储数据,建议创建一个Repository的存储区类,另外,应该确保ViewModel不会因为承担过多责任而变得臃肿。要避免这种情况,可以创建Presenter类,或者实现一种更成熟的架构。

image.png

class UserProfileViewModel: ViewModel() {
    val user = User(name="", company = "")
}

要创建一个ViewModel,首先需要扩展ViewModel类,然后将Activity中之前与UI相关的实例变量摆放在这个ViewModel中。


override fun onCreate(savedInstanceState: Bundle?) {
        //Setup Activity
        
        //ViewModelProviders.of()创建一个ViewModelProvider实例
        val userViewModel = ViewModelProviders.of(this).get(UserProfileViewModel::class.java)
        userViewModel.user.name = "小明"
    }

接着,在Activity的onCreate中从ViewModel Provider的框架实用类再获取ViewModel。
注意:ViewModelProvider将获取一个Activity实例。这种机制,让你可以旋转屏幕,获取一个新的Activity实例,不过,请确保它始终与同一个ViewModel关联。对于ViewModel实例,你可以使用getter函数,从Activity直接获取UI数据。ViewModel的默认构造函数是没有任何参数的。如果想要修改,可以使用ViewModelFactory(ViewModelProvider.Factory)创建ViewModel自定义构造函数。


上面是ViewModel最简单的用例。不过,ViewModel类也可以很好地与LiveData和DataBinding互相搭配使用。使用ViewModel和LiveData,你可以创建反应式界面。也就是说,当底层数据被更新时,UI也会相应的自动更新。


image.png

image.png

来看代码:

  1. 我们假设你的ViewModel包含LiveData,可以像平常一样利用DataBinding来绑定数据。
class UserProfileViewModel: ViewModel() {
    // _user and user are for proper encapsulation
    private val _user = MutableLiveData()
    val user:LiveData
        get() = _user

}
  1. 在这示例XML文件中,包含你的ViewModel的数据绑定布局和变量标记

        
            
            
        <.../>
    
  1. 然后,在你的Activity或Fragment中,将XML中使用的变量绑定关联
override fun onCreate(savedInstanceState: Bundle?) {
        //...
        val binding = ActivityMainBinding.inflate(layoutInflater)
        binding.viewmodel = userVieModel
        binding.setLifecycleOwner(this)//关键代码:绑定每次LiveData数据的更新
        setContentView(binding.root)
    }
  1. 再回到2.中的XML文件,这时,就可以直接从XML中的ViewModel引用LiveData字段

        
            
            

        
    

当与Binding Adapter用在一起时,你就可以在Activity中省去大量样板代码逻辑。


注意:此功能需要Android Studio 3.1或以上版本中支持。

image.png

最后,介绍一些最佳实践

注意:

  1. 不要将Context传入ViewModel,也就是说,Fragment、Activity和View都不能被传入。
    正如我们之前看到的一样,ViewModel可以比相联的Activity和Fragment的生命周期更长。
    当你旋转屏幕时,Activity将被销毁,但是,ViewModel还存储着已经被销毁的Activity的引用,这情况就是一种内存泄漏。
    另外,如果你需要比ViewModel的生命周期更长的Application类,可以使用AndroidViewModel子类,透过这个子类,你就可以直接使用Application的引用了。
  2. ViewModel并不应取代onSaveInstanceState的使用,它们俩是相辅相成的。
    • 当进程被关闭时,ViewModel将被销毁,但是onSaveInstanceState不会受到影响。
    • 另外,ViewModel可以用来存储大量数据,而onSaveInstanceState只能用来储存有限的数据。
      我们尽可能把多一点UI数据往ViewModel内存储,以便在Configuration Change时不需要重新加载或生成数据


      image.png

      image.png
  • 另一方面,如果进程被Framework关闭,我们应该用onSaveInstanceState来存储足够还原UI状态的最少量数据,例如,用户的数据库 ID


    image.png

你可能感兴趣的:(ViewModel)