Jetpack 之 LiveData

什么是 LiveData

LiveData是一个可观察的数据持有者类。与常规 observable 不同,LiveData 是生命周期感知的,这意味着它遵从其他应用程序组件的生命周期,例如 Activity,Fragment 或 Service。此感知确保 LiveData 仅更新处于 Activity 生命周期状态应用程序组件的观察者。

使用 LiveData 的好处

既然有了 observable 系列的支持,那为什么还有 LiveData 存在的意义?正所谓存在的就是合理的,也当然就有它存在的理由,下面我们来看看 LiveData 相较于 Observable 有哪些优点:

UI 和数据保持一致性

LiveData 遵循观察者模式。Observer 生命周期状态更改时,LiveData 会通知其观察的对象列表。每次应用程序数据更改时,观察者都可以在每次更改时收到消息并更新UI,而不是主动去获取状态来更新UI。

没有内存泄漏

观察者绑定 Lifecycle 对象并在其相关生命周期被销毁后自行清理。

不会因 UI 暂停而崩溃

如果观察者的生命周期处于非活动(例如,在后台堆栈中的 Activity 的情况下),则它不会接收任何 LiveData 事件。

不再需要手动管理生命周期

UI组件只是观察相关数据,不会停止或恢复观察。LiveData 自动管理所有这些动作,因为它可以感知到到相关的生命周期状态变化。

始终保持最新数据

如果 UI 生命周期变为非活动,它将在再次变为活动时接收最新数据。例如,后台 Activity 在返回前台后立即接收最新数据。

配置更改自动处理

如果由于配置更改(例如设备旋转)而重新创建 Activity 或 Fragment ,则会立即接收最新的可用数据。

共享资源

您可以把 LiveData 使用单例模式来包装各种系统服务,以便可以在应用程序中共享它们。该 LiveData 对象连接到系统服务一次,然后任何需要该资源的观察者只需观察该 LiveData 对象,即可拿到共享的数据资源。

如何使用 LiveDate 对象

包括以下步骤:

  1. 创建一个 LiveData 用于保存特定类型数据的实例。这通常在您的 ViewModel 内完成 。
  2. 创建一个 Observer 定义 onChanged() 方法的对象,该对象负责处理 LiveData 对象数据更改时发出的数据。通常Observer 在 UI 控制器中创建,例如 Activity 或 Fragment 。
  3. 使用 LiveData 的 observe 方法将 Observer 绑定到 LiveData 对象。通常在UI控制器中完成绑定,例如 Activity 或 Fragment 。

创建 LiveData 对象

在 ViewModel 中创建 LiveData 对象:

1
2
3
4
5
6
7
class NameViewModel : ViewModel() {

// Create a LiveData with a String
val currentName: MutableLiveData<String> by lazy {
MutableLiveData<String>()
}
}

观察 LiveData 对象

在 UI 中观察 LiveData:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
class NameActivity : AppCompatActivity() {

private lateinit var model: NameViewModel

override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)

// Other code to setup the activity...

// Get the ViewModel.
model = ViewModelProviders.of(this).get(NameViewModel::class.java)


// Create the observer which updates the UI.
val nameObserver = Observer<String> { newName ->
// Update the UI, in this case, a TextView.
nameTextView.text = newName
}

// Observe the LiveData, passing in this activity as the LifecycleOwner and the observer.
model.currentName.observe(this, nameObserver)
}
}

更新 LiveData 对象

如果是在非主线程中更新数据,使用 postValue 方法

1
2
3
4
button.setOnClickListener {
val anotherName = "John Doe"
model.currentName.postValue(anotherName)
}

如果是在主线程中更新数据,使用 setValue 方法

1
2
3
4
button.setOnClickListener {
val anotherName = "John Doe"
model.currentName.setValue(anotherName)
}

LiveData 高级部分

在 Room 中使用 LiviData

Room 持久库支持可观察的查询操作,返回 LiveData 对象,可观察的查询写在 DAO 中并成为 DAO 的一部分。
Room 将自动生成所有需要的中间代码,以达到数据库被更新时自动发出通知,所生成的中间代码运行在异步线程中,如果你的界面显示的数据源来自 Room,这个特性将显得非常有用。

扩展 LiviData

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
class StockLiveData(symbol: String) : LiveData<BigDecimal>() {
private val stockManager = StockManager(symbol)

private val listener = { price: BigDecimal ->
value = price
}

override fun onActive() {
stockManager.requestPriceUpdates(listener)
}

override fun onInactive() {
stockManager.removeUpdates(listener)
}
}

如上所示,如果需要扩展一个 LiveData 对象,我们需要实现以下方法:

  • onActive() 当 LiveData 对象的观察者处于活动状态时调用该方法。

  • onInactive() 当 LiveData 对象的所有观察者都处于非活动状态时调用该方法。

  • setValue(T) 此方法 LiveData 实例的值发生更新时调用,并通知所有活动的观察者。

可以像下面这样使用上面扩展的 LiveData 对象:

1
2
3
4
5
6
7
override fun onActivityCreated(savedInstanceState: Bundle?) {
super.onActivityCreated(savedInstanceState)
val myPriceListener: LiveData<BigDecimal> = ...
myPriceListener.observe(this, Observer<BigDecimal> { price: BigDecimal? ->
// Update the UI.
})
}

转换 LiveData

有时候希望在将 LiveData 对象分发给观察者之前对其中存储的值进行更改 ,或者可能需要此 LiveData 根据另一个实例的值返回其他实例。在这种场景下,Lifecycle 中包提供的 Transformations 包含支持这些方案的辅助方法和类。

  • Transformations.map()

    对存储在 LiveData 对象中的值应用一个转换函数,并将结果传播到下游。

1
2
3
4
val userLiveData: LiveData<User> = UserLiveData()
val userName: LiveData<String> = Transformations.map(userLiveData) {user ->
"${user.name} ${user.lastName}"
}
  • Transformations.switchMap()

    类似于 map(),将一个函数应用于存储在 LiveData 对象中的值,并将结果解包并调度到下游。传递给 switchMap() 参数的这个函数,必须返回一个LiveData对象,如下例所示:

1
2
3
4
5
private fun getUser(id: String): LiveData<User> {
...
}
val userId: LiveData<String> = ...
val user = Transformations.switchMap(userId) { id -> getUser(id) }

合并多个 LiveData 源

  • MediatorLiveData

    MediatorLiveData 是一个 LiveData 的子类,允许合并多个 LiveData 源。MediatorLiveData 只要其中任何一个原始 LiveData 源对象发生更改,就会触发 MediatorLiveData 对象的观察者。

例如,如果一个 LiveData 可以从本地数据库或网络更新的对象,则可以将以下源添加到该 MediatorLiveData 对象:

  • 一个与存储在数据库中的数据关联的 LiveData 对象。

  • 一个与从网络访问的数据关联的 LiveData 对象。

此时,UI 中只需要观察 MediatorLiveData 对象,就可以做到观察两个数据源的更新。

-------------本文结束感谢您的阅读-------------
坚持原创技术分享,您的支持将鼓励我继续创作!