Jetpack--ViewModel

  • ViewModel 速描

  • 资料来源:

    https://developer.android.com/topic/libraries/architecture/viewmodel#implement

  • 更新

    1
    20.02.24 初始化

导语

  • 还是官方文档总结….
  • 导入不涉及了…

基础

  • 先来看看引入 ViewModel 要解决什么问题吧?

    • 在 Activity/Fragment 中堆积了太多的逻辑,太厚了,不利于开发和单元测试.
    • 在 Activity/Fragment 重建以后的数据缓存,少量的数据可以通过 onSaveInstanceState() .但是数据量大或数据并不适合序列化,就无能为力了.
    • 涉及到业务逻辑总是会有异步调用,但 Activity/Fragment 生命周期受系统控制,处理异步调用的生命周期异常复杂.
  • ViewModel是如何解决的呢?

    • google 官方推荐的是 ViewModel 持有 LiveData,Activity/Fragment 中仅涉及一些初始化.业务逻辑等全部扔到 ViewModel 中.
    • LiveData 有数据缓存,且 ViewModel 的生命周期要比 Activity/Fragment 长的多,每次重建后直接到 ViewModel 取数据即可.
    • 异步调用已经在 ViewModel 中了,配合 rxjava 或者 kotlin 的协程等可以比较轻松的处理异步调用.
  • 创建 ViewModel 实例

    • 需要继承 ViewModel 类,一般 LiveData 声明在 ViewModel 中.

    • 例:

      1
      2
      3
      4
      5
      6
      7
      8
      9
      10
      11
      12
      13
      14
      15
      class MyViewModel : ViewModel() {
      private val users: MutableLiveData<List<User>> by lazy {
      MutableLiveData().also {
      loadUsers()
      }
      }

      fun getUsers(): LiveData<List<User>> {
      return users
      }

      private fun loadUsers() {
      // Do an asynchronous operation to fetch users.
      }
      }
    • Activity 访问

      1
      2
      3
      4
      5
      6
      7
      8
      9
      10
      11
      12
      class MyActivity : AppCompatActivity() {

      override fun onCreate(savedInstanceState: Bundle?) {
      // Create a ViewModel the first time the system calls an activity's onCreate() method.
      // Re-created activities receive the same MyViewModel instance created by the first activity.

      val model = ViewModelProviders.of(this)[MyViewModel::class.java]
      model.getUsers().observe(this, Observer<List<User>>{ users ->
      // update UI
      })
      }
      }
      • ViewModel 的生命周期要长的多,即使 Activity 被重建,得到的始终都是一个 ViewModel 实例.
  • 严重警告!: ViewModel 中绝对不能引用任何视图、Lifecycle 或可能存储对 Activity 上下文的引用的任何类.简而言之 ViewModel 不能对视图有任何感知.

  • 如果 ViewModel 需要 Application 上下文(例如,为了查找系统服务),它可以扩展 AndroidViewModel 类并设置用于接收 Application 的构造函数,因为 Application 类会扩展 Context.(这段有点没看懂,是需要上下文可以接收 Application 吗?)

  • 生命周期

    • ViewModel 对象存在的时间范围是获取 ViewModel 时传递给 ViewModelProvider 的 Lifecycle.ViewModel 将一直留在内存中,直到限定其存在时间范围的 Lifecycle 永久消失:对于 Activity,是在 Activity 完成时;而对于 Fragment,是在 Fragment 分离时.
    • 以 Activity 为例:
      viewmodel-lifecycle.png
  • ViewModel 的长生命周期和对 Activity 的单例性质,额外解决了依附于同一 Activity 的 Fragment 互相通信的问题.

    • 对于 Activity 而言 ViewModel 是单例的,那么就可以在所有 Fragment 中获取到这个单例的 ViewModel,这个过程对 Activity 是无感的,利用 ViewModel 获取到数据.并且 Fragment 的生命周期不会影响到 ViewModel.

    • 例:

      1
      2
      3
      4
      5
      6
      7
      8
      9
      10
      11
      12
      13
      14
      15
      16
      17
      18
      19
      20
      21
      22
      23
      24
      25
      26
      27
      28
      29
      30
      31
      32
      33
      34
      35
      36
      37
      38
      class SharedViewModel : ViewModel() {
      val selected = MutableLiveData<Item>()
      fun select(item: Item) { selected.value = item }
      }

      class MasterFragment : Fragment() {

      private lateinit var itemSelector: Selector

      private lateinit var model: SharedViewModel

      override fun onCreate(savedInstanceState: Bundle?) {
      super.onCreate(savedInstanceState)
      //获取ViewModel
      model = activity?.run {
      ViewModelProviders.of(this)[SharedViewModel::class.java]
      } ?: throw Exception("Invalid Activity")
      itemSelector.setOnClickListener { item ->
      // Update the UI
      }
      }
      }

      class DetailFragment : Fragment() {

      private lateinit var model: SharedViewModel

      override fun onCreate(savedInstanceState: Bundle?) {
      super.onCreate(savedInstanceState)
      //获取ViewModel
      model = activity?.run {
      ViewModelProviders.of(this)[SharedViewModel::class.java]
      } ?: throw Exception("Invalid Activity")
      model.selected.observe(this, Observer<Item> { item ->
      // Update the UI
      })
      }
      }
  • 替换加载器

    • Android 中使用数据库经常有 CursorLoader 之类的加载器使应用界面中的数据与数据库保持同步.ViewModel + Room + LiveData 一起使用可替换加载器.
    • ViewModel 确保数据在设备配置更改后仍然存在.Room 在数据库发生更改时通知 LiveData,LiveData 进而使用修订后的数据更新界面.(这个也是 jetpack 推荐的架构)
  • 与 Kotlin 协程协作.(大概是给协程挂上”生命周期”)

    • ViewModelScope: 每个 ViewModel 都定义了 ViewModelScope.如果 ViewModel 已清除,则在此范围内启动的协程都会自动取消.对于限定在 ViewModel 内的活动非常实用.

    • 例:

      1
      2
      3
      4
      5
      6
      7
      class MyViewModel: ViewModel() {
      init {
      viewModelScope.launch {
      // Coroutine that will be canceled when the ViewModel is cleared.
      }
      }
      }

结语

  • 文档用到的暂时总结到此.
  • 下一篇是 Room .