implementation "androidx.lifecycle:lifecycle-livedata-ktx:$lifecycle_version"
一、概念
一个可以被观察的数据持有类,LiveData是只读版本,MutableLiveData是可读可写版本,。
- 用来更新UI:setValue()和postValue()更新值都是在主线程执行,因此观察者的onChange()回调在主线程。
- 生命周期感知:可以感知 Activity、Fragment或Service 等组件的生命周期,无需手动处理,UI层只需要对数据进行监听。
- 单个最新值:更新UI需要使用最新数据,过时的数据应该被忽略。并且只会在界面处于活跃状态(onStart和OnResume)收到通知(非活跃状态更新UI无意义浪费资源)。
- 自动取消订阅:Observers 是绑定到 Lifecycle 对象上的,当与其关联的 lifecycle 被销毁的时候,它们会自动被清理。开发者无需手写模板代码,降低内存泄漏风险。
- 粘性事件:当按下Button弹出Snackerbar,此时设备配置改变(例如屏幕旋转),之前的View会销毁然后重建,新的View会重新订阅,那么会再次消费事件弹出Snackerbar。
- 默认不防抖:更新的值和上次相同,onChange()依然会再次调用。可以使用Transformations的distinctUntilChanger()、SingleLiveEvent解决。
observe ( ) | 对LiveData进行观察(订阅)。 |
oberverForever ( ) | 无论界面处于什么状态都能收到通知,因此用完一定要记得调用removeObserver()来取消订阅,否则LiveData会一直处于激活状态,Activity永远不会被系统自动回收,这就造成了内存泄漏。 |
removeObserve ( ) | 移除当前所有观察者。 |
hasObserve ( ) | 如果有观察者。 |
hasActiveObserve ( ) | 如果有活跃的观察者(UI处于onStart或者onResume)。 |
getValue ( ) | 获取数据。 |
setValue ( ) | 设置数据(主线程中调用)。 |
postValue ( ) | 设置数据(子线程中调用)。 |
二、创建
2.1 MutableLiveData
LiveData是只读版本,MutableLiveData是可读可写版本,根据实际需求暴露不同类型收窄功能。没有初始化值默认为null。
ViewModel {
//权限封装
private val _num = MutableLiveData<Int>() //私有化可读可写版本
val num: LiveData<Int> = _num //对外暴露只读版本
//初始化值
init {
viewModelScope.launch {
_num.value = getData() //调用挂起函数赋值
}
}
//数据源(Suspend)
suspend fun getData(): Int = withContext(Dispatchers.IO) { 3 }
//数据源(Flow)
val data: Flow<Int> = flow { repeat(3) { emit(it) } }
}
UI {
viewModel.num.observe(this, Observer { num -> ... })
viewModel.num.observer(this) { num -> ... } //ktx写法
}
2.2 MediatorLiveData
继承自MutableLiveData,增加了合并多个LivaData数据源的功能,可以同时监听多个LiveData的数据变化。例如分别为本地和网络两个数据创建LiveData,通过MutableLiveData调用addSource()将两个LiveData数据源添加进去,最后Ovserve即可。
ViewModel {
val local = MutableLiveData<String>()
val remote = MutableLiveData<String>()
val combine = MediatorLiveData<String>()
combine.addSource(local) { combine.value = it }
combine.addSource(remote) { combine.value = it }
}
UI {
viewModel.combine.observe(this, Observe { value ->
})
}
2.3 Transformations
工作在主线程,对LiveData持有的数据应用函数。
map | LiveData持有的数据信息过多,只需要取其中一部分。例如持有User只需要User.name。 |
switchMap | 使用LiveData持有的数据来获取另一个LiveData对象。 |
distinctUntilChanged | 更新的值和当前值相同则不会回调onChange()。 |
//Transformations.map
val userLiveData: LiveData<User> = MutableLiveData(User())
val nameLiveData: LiveData<String> = Transformations.map(userLiveData) { user ->
user.name
}
//Transformations.switchMap
fun getUser(id: Long): LiveData<User> {...}
val idLiveData: LiveData<Long> = MatableLiveData(123456)
val userLiveData: LiveData<User> = Transformations.switchMap(idLiveData) { id ->
getUser(id)
}
2.4 Suspend → LiveData(协程构建器)
LifeCycle v2.20 版本后,可以在定义LiveData的同时使用挂起函数的结果赋值(由于只读无法更新,适用于一次性数据),省去了对只读限制的封装,Activity中直接观察ViewModel的该属性。
public fun <T> liveData( context: CoroutineContext = EmptyCoroutineContext,//区块所执行的协程上下文,默认+Main.immediate timeoutInMs: Long = DEFAULT_TIMEOUT, //没有观察者后,多少毫秒后取消区块,默认5s @BuilderInference block: suspend LiveDataScope<T>.() -> Unit //区块在LiveData被观察的时候执行 ): LiveData<T> |
class MyViewModel : ViewModel() {
//直接调用挂起函数赋值
val num1 = liveData { emit(getData()) }
//还可以指定线程,单独写耗时操作
val num2: LiveData<Int> = liveData(Dispatchers.IO) {
emit(5)
delay(1000)
emit(3)
}
//从另一个LivaData获取更新结果
val aa = MutableLiveData(10)
val bb = liveData{ emitSource(aa) }
suspend fun getData(): Int = withContext(Dispatchers.IO) { 3 }
}
2.6 Flow → LiveData(扩展函数)
public fun <T> Flow<T>.asLiveData( context: CoroutineContext = EmptyCoroutineContext,//区块所执行的协程上下文,默认+Main.immediate timeoutInMs: Long = DEFAULT_TIMEOUT //没有观察者后,多少毫秒后取消区块,默认5s ): LiveData<T> |
class MyViewModel : ViewModel() {
val num1: LiveData<Int> = liveData { DataSource().getDataFlow.collect { emit(it) } }
val num2: LiveData<Int> = DataSource().getDataFlow.asLiveData() //简写
}
class DataSource {
val getDataFlow: Flow<Int> = flow { repeat(3) { emit(it) } }
}