系列文章
【背上Jetpack】Jetpack 主要组件的依赖及传递关系
【背上Jetpack】AdroidX下使用Activity和Fragment的变化
【背上Jetpack之Fragment】你真的会用Fragment吗?Fragment常见问题以及androidx下Fragment的使用新姿势
【背上Jetpack之Fragment】从源码角度看 Fragment 生命周期 AndroidX Fragment1.2.2源码分析
【背上Jetpack之OnBackPressedDispatcher】Fragment 返回栈预备篇
【背上Jetpack之Fragment】从源码的角度看Fragment 返回栈 附多返回栈demo
【背上Jetpack】绝不丢失的状态 androidx SaveState ViewModel-SaveState 分析
【背上Jetpack之ViewModel】即使您不使用MVVM也要了解ViewModel ——ViewModel 的职能边界
前言
之前我们讨论过 ViewModel 的职能边界 ,得益于 ViewModel 的生命周期更长,我们可以在 activity 重建后将数据传递给 activity ,也可以避免内存泄漏。但是如果不是每次需要就获取数据,而是当每次有新数据时通知我们,应该怎么办?
本文介绍 LiveData
,一个 生命周期感知的,可观察的,数据持有者。同时还会简单分析 LiveData
的源码实现
我们都是 Adapter
在谈 LiveData
前我们来思考一个问题
Android 开发(亦或者说前端开发)的本质工作内容是什么?
对于应用层 app 开发者,开发者的工作主要工作就是 Adapter
什么是 Adapter ,下图可能比较直观
图片来自 google image
我们的工作本质是 将数据转换成 UI
数据可能来自网络,来自本地数据库,来自内存,而 UI 可能是 activity 或 fragment。
理想的数据模型
上面我们提到 Android 开发者的核心工作就是将数据转换为 UI 。这个过程比较理想的状态是:当数据发生变化时,UI 跟随变化。我们还可以进一步展开:当 UI 对用户可见时,数据发生变化时 UI 跟随变化;当 UI 对用户不可见时,我们希望数据变化时什么都不做,当 UI 再次对用户可见时根据最新的数据进行 UI 的处理。
而 LiveData
就是我们理想中的数据模型
LiveData 可以三个关键词概括
-
lifecycle-aware
-
observable
-
data holder
observable
Android 中不同的组件有着不同的生命周期,不同的存活时间
因此我们不会在 ViewModel
中持有 Activity
的引用,因为这会导致当 Activity
重建时内存泄漏,甚至出现空指针的情况
通常我们会在 Activity
中持有 ViewModel
的引用,那么如何进行二者间的通信,如何向 Activity
发送 ViewModel
中的数据?
答案是让 Activity
观察 ViewModel
LiveData
是 observable
lifecycle-aware
当观察者观察着某个数据时,该数据必须保留对观察者的引用才能调用它,为了解决这个问题,LiveData
被设计成可感知生命周期
当 activity / fragment 被销毁后,它会自动的取消订阅
data holder
LiveData
仅持有 单个且最新 的数据
上图中,最右侧是在 ViewModel
中的 LiveData
,左侧为观察这个 LiveData
的 activity / fragment 。一旦我们为 LiveData
设值,该值会传递到 activity。简而言之,LiveData
值改变,activity 收到最新的值的变化。但是当观察者不再处于活动状态(STARTED 到 RESUMED ),数据 C 不会被发送到 activity 。当 activity 回到前台,它将收到最新的值,数据 D。LiveData 仅持有单个且最新的数据。当 activity 执行销毁流程时,此时的数据 E 也不会产生任何影响
Transformations
LiveData
提供 两种 transformation ,map
和 switch map
。开发者也可以创建自定义的 MediatorLiveData
我们都知道 LiveData
可以为 View 和 ViewModel 提供通信,但如果有一个第三方组件(例如 repository )也持有 LiveData
。那么它应该如何在 ViewModel
中订阅?该组件并没有 lifecycle
一旦我们的应用愈发复杂,repository 可能会观察数据源
那么 view 如何获取 repository 中的 LiveData
?
一对一的静态转换(map)
在上面的示例中,ViewModel
仅将数据从 repository 转发到 view,然后将其转换为 UI Model。 每当 repository 中有新数据时,ViewModel
只需 map
class MainViewModel {
val viewModelResult = Transformations.map(repository.getDataForUser()) {
data ->
convertDataToMainUIModel(data)
}
}
第一个参数为 LiveData
源(来自 repository ),第二个参数是一个转换函数。
// 这里的转换为将 X 转换为 Y
inline fun <X, Y> LiveData<X>.map(crossinline transform: (X) ->