LiveData 是一个可观察数据包装类,与普通观察者不同,LiveData 具备生命周期感知能力,这意味着它遵循其它应用组件的生命周期(Activity、Fragment、Service 等),此感知能力确保了 LiveData 只更新处于生命周期活跃状态的组件的观察者
生命周期活跃状态的定义是这些组件正处于 STARTED 或 RESUMED 状态,LiveData 只会更新活跃状态的观察者,而已注册但处于非活跃状态的观察者不会被更新
我们可以在实现了 LifecycleOwner 的接口的对象中注册 observer,这种关联允许 observer 在与之相关的 Lifecycle 对象处于 DESTROYED 状态时自动移除,尤其是 activities 和 fragments ,它们可以安全地观察 LiveData 而不必担心内存泄露 —— activities 和 fragments 在生命周期销毁时会立即取消数据观察订阅
LiveData 遵循观察者模式。生命周期状态更改时,LiveData 会通知 Observer 对象。你可以合并代码以更新这些 Observer 对象中的 UI。每次应用程序数据更改时,你的观察者都可以在每次更改时更新 UI,而不是更新 UI
观察者绑定到 Lifecycle 对象,并在其相关生命周期被破坏后自行清理
如果观察者的生命周期处于非活动状态,例如在后端堆栈中的活动的情况下,则它不会收到任何 LiveData 事件
UI 组件只是观察相关数据,不会停止或恢复观察。 LiveData 自动管理所有这些,因为它在观察时意识到相关的生命周期状态变化
如果生命周期变为非活动状态,它将在再次变为活动状态时接收最新数据。例如,后台活动在返回前台后立即接收最新数据
如果由于配置更改(例如设备轮换)而重新创建活动或片段,则会立即接收最新的可用数据
你可以使用单例模式扩展 LiveData 对象以包装系统服务,以便可以在应用程序中共享它们。 LiveData 对象连接到系统服务一次,然后任何需要该资源的观察者都可以只观看 LiveData 对象
使用 LiveData 的常规步骤如下,我们按照下面这些步骤写一个简单的例子:
1、创建持有任意类型的 LiveData 实例,这一步通常在 ViewModel 中完成
2、创建一个 Observer 对象并重写其 onChanged()
方法,该方法会在 LiveData 数据更改时被回调并返回最新数据,这一步通常在 UI 控制器中完成
3、使用 observer()
方法关联 Observer 和 LiveData ,observer()
方法持有 LifecycleOwner 对象,此订阅会让 LiveData 在数据变动时及时通知 Observer,这一步通常在 UI 控制器中完成
注意:你可以使用 observeForever(Observer) 方法注册没有关联 LifecycleOwner 对象的观察者。在这种情况下,观察者被认为始终处于活动状态,因此始终会收到有关修改的通知。你可以通过 removeObserver(Observer) 方法删除观察者
LiveData 是一个包装器,可以包装任何数据,包括实现集合的对象,例如 List。 LiveData 对象通常存储在ViewModel 对象中,并通过 getter 方法访问,如以下示例所示:
class NameViewModel : ViewModel() {
private var _name = MutableLiveData<String>()
var name: LiveData<String> = _name
// 提供一个用来修改名字的方法
fun changeName() {
_name.value = if (_name.value == "pony") {
"tony"
} else {
"pony"
}
}
}
在大多数情况下,从 app 组件的 onCreate()
方法是开始观察 LiveData 对象最合适,原因如下:
onResume()
可能被多次调用,造成 Observer 重复注册class MainActivity : AppCompatActivity() {
// 创建 ViewModel
private val viewModel by lazy {
ViewModelProviders.of(this).get(NameViewModel::class.java)
}
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)
val textView = findViewById<TextView>(R.id.tv_name)
// 给 textView 一个初始值
textView.text = "tony"
// 每次点击 textView 时,都更新一下 name
textView.setOnClickListener {
viewModel.changeName()
}
// 创建更新 UI 的观察者
val nameObserver = object : Observer<String> {
override fun onChanged(t: String?) {
// 更新 UI,设置最新的 name 给 textView
textView.text = t
}
}
// 观察 LiveData ,传递 lifecycleOwner 和 nameObserver
viewModel.name.observe(this, nameObserver)
}
}
nameObserver 作为 observe()
的参数注册之后,一旦 name
发生变动且 MainActivity 处于活跃状态,onChanged()
方法就会被立即调用,然后 UI 会被更新
Room 持久化库支持可观察查询并返回 LiveData 对象,这些查询一般在 DAO 中编写
在更新数据库时,Room 会生成更新 LiveData 对象所需的所有代码,此模式对于使 UI 中显示的数据与存储在数据库中的数据保持同步非常有用。具体可以参考 Android Jetpack - Room 一文所提供的代码示例
您可能希望在将 LiveData 对象分派给观察者之前更改存储在 LiveData 对象中的值,或者您可能需要根据另一个 LiveData对象的值返回不同的 LiveData 实例。 Lifecycle 包提供 Transformations 类,其中包括支持这些方案的帮助程序方法
Transformations.map()
比如将点赞数量转换成流行度
class MainViewModel : ViewModel() {
private val _likes = MutableLiveData(0)
// 声明 popularity 并通过 Transformations 创建其与 likes 的关系
val popularity: LiveData<Popularity> = Transformations.map(_likes) {
when {
it > 9 -> Popularity.STAR
it > 5 -> Popularity.POPULAR
else -> Popularity.NORMAL
}
}
}
enum class Popularity {
NORMAL, // 普通
POPULAR, // 受欢迎
STAR // 巨星
}
Transformations.switchMap()
与 map()
类似,将函数应用于存储在 LiveData 对象中的值,并将结果解包并调度到下游。传递给 switchMap()
的函数必须返回一个 LiveData 对象,如以下示例所示:
private fun getUser(id: String): LiveData<User> {
...
}
val userId: LiveData<String> = ...
val user = Transformations.switchMap(userId) { id -> getUser(id) }
https://github.com/realskyrin/jetpack_livedata
https://developer.android.com/topic/libraries/architecture/livedata
https://codelabs.developers.google.com/codelabs/android-room-with-a-view-kotlin
https://medium.com/androiddevelopers/viewmodels-and-livedata-patterns-antipatterns-21efaef74a54