Android中LiveData的使用技巧
前言
最近一直在写
kchat
即时通讯软件,在项目中,使用到了LiveData,这篇文章主要来介绍一下LiveData的使用技巧,以及某些问题的解决方案。
什么是LiveData?
LiveData
是 Android Jetpack 提供的一种数据持有类,它可以感知生命周期并自动管理 UI 更新。LiveData
主要用于将数据传递给 UI 层,并确保数据更新时,只有处于活动状态的界面组件(如 Activity
、Fragment
)才会收到通知,从而避免内存泄漏和不必要的 UI 更新。
- 生命周期感知:
LiveData
会自动与生命周期(如onStart
、onStop
)绑定,只有在相关组件活跃时才会发送更新。 - 数据持有:它持有数据并提供观察者机制,UI 可以观察
LiveData
并在数据变化时更新界面。 - 线程安全:
LiveData
可以在后台线程中更新数据,保证线程安全性,并自动切换到主线程进行 UI 更新。
setValue(T value)
:在主线程中更新数据,通知观察者。postValue(T value)
:在非主线程中更新数据,通知观察者(其原理就是自动切换到主线程进行发送value)。observe(LifecycleOwner owner, Observer<T> observer)
:注册观察者,监听数据变化。
LiveData的简单使用
下面我拿我开发中的一个’小栗子’——修改密码功能来演示怎么进行使用LiveData
一般来说在Android项目中LiveData一般都是配合ViewModel使用的,我们新建一个
UserViewModel
然后继承自ViewModel()
,我这里的代码其实写的不规范(违反了依赖倒置原则),只是给大家演示一下- 在上图中我们使用
viewModelScope
这个是可以使用在viewModel里的协程,然后在协程里面,将调用网络接口返回的数据post到livedata中 - 向livedata中发送数据的话有两种方法,一种是上图中的,另一种就是
_updatePasswordResult.value = result
,他俩的区别在于,postValue是发生在主线程中的,然后setValue不是在主线程中的 - 我们可以看到有下图这种写法,为什么要这样写呢,其实kotlin中的get方法是直接自动获取原属性的值的,但是通过get方法的数据无法进行修改,这样保证了数据的一致性与完整性,在我看来,这也符合mvvm架构中的数据单项流通性
- 在上图中我们使用
在activity中声明viewModel,在这里直接使用委托
private val userViewModel: UserViewModel by viewModels()
监听数据
发送网络请求
解决“多次返回重复数据”问题
举个例子,假设用户进入一个聊天页面后,聊天数据通过LiveData进行观察。此时数据更新正常。但如果用户进入另一个页面后再返回聊天页面触发了第二次监听,LiveData会重复通知他们相同的数据。随着页面跳转的次数增加,这个问题会变得更加明显,导致UI更新过多,影响性能和用户体验。
问题原因:
导致这个问题的根本原因在于我们新建了一个LiveData,并只向其中发送了一个数据。在这个过程中,没有新的数据进入该LiveData替代原有数据,导致这个数据一直存在于LiveData中,同时我们又对LiveData进行了多次监听,就导致该问题的发生。由于LiveData本身的特点,它会保持该数据直到被其他数据更新或者被移除,因此当Activity重新进入时,会重复监听到同样的数据。解决方法:
我最初的想法是:是否能让LiveData在数据被消费后当条数据立即“消失”,避免重复通知。通过查阅资料,我找到了Google大神提供的方案,虽然最初是Java版的实现,但我将其转换为了Kotlin版本(详见下文的解决方案部分)。 另外,我也曾尝试向GPT寻求帮助,然而得到的答案让我有些失望。GPT的建议是:每次接收到数据后,移除对LiveData的监听。但是,这个做法在我看来并不合理,因为移除监听会导致每次重新设置监听,这样会造成不必要的性能浪费,特别是在频繁需要监听数据变化的场景下。相比之下,Google的方案提供了更优雅的解决方式,通过对LiveData的改造,确保数据只会在被明确消费后触发更新,从而避免了数据的重复通知。import androidx.annotation.MainThread
import androidx.lifecycle.LifecycleOwner
import androidx.lifecycle.MutableLiveData
import androidx.lifecycle.Observer
import java.util.concurrent.atomic.AtomicBoolean
/**
* A lifecycle-aware observable that sends only new updates after subscription, used for events like
* navigation and Snackbar messages.
* <p>
* This avoids a common problem with events: on configuration change (like rotation) an update
* can be emitted if the observer is active. This LiveData only calls the observable if there's an
* explicit call to setValue() or call().
* <p>
* Note that only one observer is going to be notified of changes.
*/
class SingleLiveEvent<T> : MutableLiveData<T>() {
private val mPending = AtomicBoolean(false)
override fun observe(owner: LifecycleOwner, observer: Observer<in T>) {
super.observe(owner) { t ->
// 只有在 mPending 为 true 时才会触发回调
if (mPending.compareAndSet(true, false)) {
observer.onChanged(t)
}
}
}
@MainThread
override fun setValue(value: T?) {
mPending.set(true)
super.setValue(value)
}
@MainThread
fun call() {
setValue(null)
}
}
- 感谢你赋予我前进的力量
赞赏名单
因为你们的支持让我意识到写文章的价值🙏
本文是原创文章,采用CC BY-NC-SA 4.0协议,完整转载请注明来自糕菜菜
评论 ()