概述

一般来说,LiveData很少单独使用,它更多的和Android Jetpack的其他组件搭配使用,比如ViewModel和ViewBinding。所以前面我们介绍ViewModel的使用以及其实现原理。那么这篇文章就来介绍LiveData的使用。

LiveData是什么?

通过字面意思其实我们可以理解成生存(活着)的数据。我们看下官方是怎么介绍它的:

LiveData 是一种可观察的数据存储器类。与常规的可观察类不同,LiveData 具有生命周期感知能力,意指它遵循其他应用组件(如 Activity、Fragment 或 Service)的生命周期。这种感知能力可确保LiveData仅更新处于活跃生命周期状态的应用组件观察者。

化繁为简:

  • LiveData具备生命周期的感知能力
  • LiveData只存在活跃的生命周期里,比如STARTED或RESUMED。

LiveData的优势

使用 LiveData 具有以下优势:

  • 确保界面符合数据状态
    LiveData 遵循观察者模式。当底层数据发生变化时,LiveData会通知Observer对象。您可以整合代码以在这些Observer对象中更新界面。这样一来,您无需在每次应用数据发生变化时更新界面,因为观察者会替您完成更新。

  • 不会发生内存泄漏
    观察者会绑定到 Lifecycle 对象,并在其关联的生命周期遭到销毁后进行自我清理。
    不会因 Activity 停止而导致崩溃如果观察者的生命周期处于非活跃状态(如返回栈中的 Activity),则它不会接收任何LiveData事件。

  • 不再需要手动处理生命周期
    界面组件只是观察相关数据,不会停止或恢复观察。LiveData将自动管理所有这些操作,因为它在观察时可以感知相关的生命周期状态变化。

  • 数据始终保持最新状态
    如果生命周期变为非活跃状态,它会在再次变为活跃状态时接收最新的数据。例如,曾经在后台的Activity会在返回前台后立即接收最新的数据。

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

  • 共享资源
    您可以使用单例模式扩展LiveData对象以封装系统服务,以便在应用中共享它们。LiveData对象连接到系统服务一次,然后需要相应资源的任何观察者只需观察LiveData对象。

如何使用LiveData

前面我们介绍了LiveData是什么以及它的优势有什么,那么接下来我们介绍下到底如何使用它。

基本使用

LiveData是一个抽象类,不能直接使用,我们一般使用它的直接子类MutableLiveData。那我们直接写例子,直接在之前的ViewModel里面修改代码:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
/**
* @date:2021/2/22
* @author:Silence
* @describe:
**/
class MyViewModel : ViewModel() {

var userData: MutableLiveData<UserInfo> = MutableLiveData()

fun getUserInfo() {
val user = UserInfo("我就是马云飞", (1..100).random())
userData.postValue(user)
}

fun updateUserInfo(userInfo: UserInfo) {
userData.postValue(userInfo)
}
}

然后我们看看怎么在Activity里面获取这个data呢。接着看代码:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
private val viewModel by lazy { ViewModelProvider(this).get(MyViewModel::class.java) }
private var count = 0

override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_view_model)
viewModel.userData.observe(this, Observer {
Log.i(TAG, "onCreate: " + "姓名:${it.name} \t年龄:${it.age}")
})
liveDataBtn.setOnClickListener {
if (count % 2 == 0) {
viewModel.getUserInfo()
} else {
val user = UserInfo("你不是马云飞", (1..100).random())
viewModel.updateUserInfo(user)
}
count++
}
}

我们在Activity中对viewModel的userData做了监听,实时观察数据的变化。那么我们现在进行点击看看效果如何:

image

果然,只要我数据更新了,我的Log立马就输出了。

扩展使用

如果观察者的生命周期处于STARTED或RESUMED状态,则LiveData会认为该观察者处于活跃状态。我们直接看官方的例子:

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
39
40
41
42
class StockLiveData(symbol: String) : LiveData<BigDecimal>() {
//股票管理类
private val stockManager: StockManager = StockManager(symbol)

//监听股票价格的变化
private val listener = { price: BigDecimal ->
//更新value值
value = price
}

//页面处于活跃状态时,开始观察股票价格的数据更新
override fun onActive() {
stockManager.requestPriceUpdates(listener)
}

//页面处于非活跃状态,移除相关监听
override fun onInactive() {
stockManager.removeUpdates(listener)
}

companion object {
private lateinit var sInstance: StockLiveData

//这是一个单例
@MainThread
fun get(symbol: String): StockLiveData {
sInstance = if (::sInstance.isInitialized) sInstance else StockLiveData(symbol)
return sInstance
}
}
}

//Fragment中的使用
class MyFragment : Fragment() {

override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState)
StockLiveData.get(symbol).observe(viewLifecycleOwner, Observer<BigDecimal> { price: BigDecimal? ->
// Update the UI.
})

}

我们看下上述的代码,具体包含如下功能:

  • 本身是个单例,也就是可以在多个Activity与Fragment中进行数据监听。

  • 当具备活跃的观察者时,会调用onActive方法,监听股价的变动。

  • 当其观察者不具备活跃状态时,会调用onInactive方法,移除监听。也就是就算值改变了,也不会同步更新,同理,当页面销毁时,会自动移除监听。

数据转换

map

我们按照上面我们的示例代码继续,前面我们打印了姓名和年龄,那么假设,我暂时不需要字段,我需要通过姓名拿到他的班级以及学号。也就是通过姓名将数据转换成班级和学号,LiveData也给我们提供了相关方法,可以通过Transformations.map去实现。具体代码如下:

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
// ViewModelActivity.kt
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_view_model)
viewModel.userData.observe(this, Observer {
Log.i(TAG, "onCreate: " + "姓名:${it.name} \t年龄:${it.age}")
})
viewModel.getUserInfo()
viewModel.classInfo.observe(this, Observer {
Log.i(TAG, "onCreate: " + "班级:${it?.grade} \t学号:${it?.id}")
})
liveDataBtn.setOnClickListener {
viewModel.getClassIdByUser().observe(this, Observer {
})
}
}

// MyViewModel.kt
fun getClassIdByUser(): LiveData<ClassBean?> {
return Transformations.map(userData) { input ->
var classBean: ClassBean? = null
if (input?.name == "我就是马云飞") {
classBean = ClassBean("高三", (1..100).random())
classInfo.postValue(classBean)
}
classBean
}
}

首先我们进入页面先获取一次用户的姓名和年龄,然后当我们点击按钮的时候,我们根据用户的姓名给他定义一个班级和学号。我们来看下效果:

可以看到,每次点击的时候,它都会给我们生成一个学号。说明我们的数据转换成功了。

switchMap

switchMap的方法和map大同小异,只是它是返回一个liveData,而上面map是直接返回T。我们直接上代码:

1
2
3
4
5
6
7
fun getClassIdByUser(): LiveData<ClassBean?> {
return Transformations.switchMap(userData) {
val classBean = ClassBean("高三", (1..100).random())
classInfo.postValue(classBean)
classInfo
}
}

输出也基本差不多。

数据合并

所谓的数据合并是什么呢?之前我们如果有多套的LiveData,我们需要对多套数据进行观察。而数据合并可以做到批量添加LiveData并且只实现一组观察逻辑就可以监听多套数据的改动。我们修改一下代码:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
viewModel.mediatorLiveData.addSource(viewModel.userData) {
Log.i(TAG, "卧槽,数据合并也可以监听!!!!onCreate: " + "姓名:${it.name} \t年龄:${it.age}")
}
viewModel.mediatorLiveData.addSource(viewModel.classInfo) {
Log.i(TAG, "卧槽,数据合并也可以监听!!!!onCreate: " + "班级:${it?.grade} \t学号:${it?.id}")
}
viewModel.mediatorLiveData.observe(this, Observer<Any?> {
})
liveDataBtn.setOnClickListener {
if (count % 2 == 0) {
viewModel.getUserInfo()
} else {
viewModel.getClassInfo()
}
count++
}

我们继续通过点击看看效果如何:

哎。可以了~

总结

本文我们主要介绍了LiveData是什么,以及它的优势和使用方法。而LiveData的很多细节我们没有去关注,比如postValue和setValue。比如Transformations的map和switchMap方法。后面我们会在LiveData原理篇讲到。毕竟我们需要了解它是怎么使用的。会使用后才会想去了解其原理。

参考

官网