概述
一般来说,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
|
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做了监听,实时观察数据的变化。那么我们现在进行点击看看效果如何:
果然,只要我数据更新了,我的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 = 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 } } }
class MyFragment : Fragment() {
override fun onViewCreated(view: View, savedInstanceState: Bundle?) { super.onViewCreated(view, savedInstanceState) StockLiveData.get(symbol).observe(viewLifecycleOwner, Observer<BigDecimal> { price: BigDecimal? -> })
}
|
我们看下上述的代码,具体包含如下功能:
本身是个单例,也就是可以在多个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
| 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 { }) } }
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原理篇讲到。毕竟我们需要了解它是怎么使用的。会使用后才会想去了解其原理。
参考
官网