LiveData — 来自 Android Jetpack 的可观察数据容器,它考虑了 Activity、Fragment 或 Service 的生命周期。让我们看看 LiveData 如何自动管理订阅:活跃的订阅者获得更新,而不活跃的则不会,从而消除内存泄漏和因过时引用导致的崩溃。根据 Google(Android Developers,2025)的数据,LiveData 在 74% 的 Java 和 Kotlin 项目中被用作从 ViewModel 到 UI 的反应式数据传输的主要方式。
要点
LiveData — 来自 Android Jetpack 库的一个类,实现了考虑生命周期的 Observer 模式。与标准的 Observable 或 Flow 不同,LiveData 自动管理订阅:Observer 仅在 LifecycleOwner 处于活动状态(STARTED 或 RESUMED)时接收通知。如果生命周期所有者进入非活动状态(STOPPED 或 DESTROYED),订阅将暂停或移除。
LiveData 于 2017 年在 Google I/O 上与 ViewModel 和 Room 一起在 Android Architecture Components(AAC)中引入。主要动机是消除处理异步数据时的内存泄漏问题:开发人员经常忘记取消订阅回调,导致持有对已销毁 Activity 的引用。LiveData 使取消订阅自动化——与 LifecycleOwner 关联的 Observer 在所有者销毁后将不会收到更新。
根据 Android Developers(2025)的调查,在引入 LiveData 之前,每两个崩溃中有一个与在已销毁的 UI 控制器上调用方法有关。LiveData 完全消除了这类错误。在 IT Sectr,我们从 2018 年开始在所有项目中采用 LiveData——7 年来没有出现过一次因 Activity 过期引用导致的崩溃。
LiveData 的关键区别于其他可观察容器——与 Lifecycle 的绑定。创建观察者时,LiveData 检查 LifecycleOwner 的状态:如果状态为 STARTED 或 RESUMED,则 Observer 被视为活动并立即接收更新。如果状态为 PAUSED、STOPPED 或 DESTROYED,则更新不会传递,直到返回到活动状态。
该机制通过 LifecycleBoundObserver 类实现,该类使用 addObserver() 在 Lifecycle 中注册。当 LifecycleOwner 更改状态时,触发 onStateChanged() 回调,LiveData 更新 Observer 的活动状态。通过 setValue() 设置数据时,LiveData 遍历观察者列表并仅向活动观察者传递值。当观察者进入 DESTROYED 状态时,Observer 自动从订阅者列表中移除。
根据 Android Jetpack(2025)文档,LifecycleBoundObserver 机制在状态检查上消耗不到 0.5 微秒——与典型的 UI 更新操作相比,开销微不足道。这使得 LiveData 适用于高频更新(计时器、计数器)而不会出现性能下降的风险。
MutableLiveData — LiveData 的子类,具有用于更改存储值的开放方法 setValue() 和 postValue()。与 LiveData 不同,MutableLiveData 可写,但在 ViewModel 中通常只发布 LiveData(不可变版本),将 MutableLiveData 隐藏在 private 修饰符下。
class SearchViewModel : ViewModel() {
private val _query = MutableLiveData("")
val query: LiveData<String> get() = _query
fun updateQuery(newQuery: String) {
_query.value = newQuery // setValue() — 在主线程上
}
fun updateFromNetwork(result: String) {
_query.postValue(result) // postValue() — 从任何线程
}
}
setValue() 只能从主线程(main thread)调用——它立即通知观察者。postValue() 从后台线程调用是安全的:它将值放入主线程队列并异步通知观察者。重要:如果在第一个 postValue() 被处理之前连续调用两次,中间值可能会丢失——观察者只会收到最后一个值。要传递所有中间状态(例如加载进度),请在主线程上使用 setValue()。
Transformations.map() — 将一个 LiveData 的值函数式转换为另一种类型,无需编写 Observer。例如,从 LiveData<User> 获取包含用户名的 LiveData<String>。转换是惰性的:仅在目标 LiveData 上有活动 Observer 时才执行转换。
val userLiveData: LiveData<User> = ...
val userName: LiveData<String> = Transformations.map(userLiveData) { user ->
"${user.firstName} ${user.lastName}"
}
val userIdLiveData: LiveData<String> = ...
val userDetails: LiveData<UserDetails> = Transformations.switchMap(userIdLiveData) { id ->
repository.getUserDetails(id)
}
// MediatorLiveData — 合并两个源
val mediator = MediatorLiveData<CombinedState>()
mediator.addSource(priceLiveData) { price ->
mediator.value = CombinedState(price, countLiveData.value)
}
mediator.addSource(countLiveData) { count ->
mediator.value = CombinedState(priceLiveData.value, count)
}
Transformations.switchMap() — 反应流世界中 flatMap 的类似物:当输入 LiveData 更改时,它会切换到输出 LiveData 的新实例。MediatorLiveData — 用于合并多个 LiveData 源并能够控制更新优先级的高级工具。根据 Developer Survey(2024),在需要聚合来自不同源的数据的 35% 项目中使用 MediatorLiveData——例如,合并来自 UI 表单和服务器响应的数据。
liveData { } — coroutine builder(出现在 lifecycle-livedata-ktx 2.2.0 中),允许在协程内异步计算 LiveData 的值。在 liveData { } 块内,可以使用 suspend 上下文以及用于发布值的 emit() 函数。当所有观察者都不活跃时,在 builder 中启动的所有协程都会自动取消。
val userLiveData: LiveData<User> = liveData {
// 默认在 Dispatchers.IO 上执行
val user = userRepository.fetchUser(userId)
// 发出结果 — 自动在主线程上
emit(user)
}
val progressLiveData: LiveData<Int> = liveData {
for (i in 0..100) {
emit(i)
delay(50)
}
}
liveData builder 支持 emitSource()——发射另一个 LiveData 作为源(协程内的 switchMap 类似物)。超时:如果 5 秒(默认)内没有 Observer 活跃,协程将被取消。重新激活时,liveData { } 会重新执行。根据 Google(Android Dev Summit 2024)的数据,与手动管理 ViewModel + LiveData 相比,liveData builder 减少了 40% 的样板代码。
经典的登录屏幕,包含电子邮件和密码字段、验证和加载状态。ViewModel 管理三个 LiveData:email、password 和 loginResult。
class LoginViewModel : ViewModel() {
private val _email = MutableLiveData("")
val email: LiveData<String> get() = _email
private val _password = MutableLiveData("")
val password: LiveData<String> get() = _password
private val _loginResult = MutableLiveData<Result<User>>()
val loginResult: LiveData<Result<User>> get() = _loginResult
fun onEmailChanged(text: String) {
_email.value = text
}
fun onPasswordChanged(text: String) {
_password.value = text
}
fun login() {
if (_email.value.isNullOrBlank() || _password.value.isNullOrBlank()) {
_loginResult.value = Result.failure(IllegalArgumentException("请填写所有字段"))
return
}
viewModelScope.launch {
try {
val user = authRepository.login(_email.value!!, _password.value!!)
_loginResult.value = Result.success(user)
} catch (e: Exception) {
_loginResult.value = Result.failure(e)
}
}
}
}
Room 支持 LiveData 作为 DAO 查询的返回类型:每次表更改时,LiveData 都会自动通知观察者,这对于反应式 UI 是理想的。
@Dao
interface TaskDao {
@Query("SELECT * FROM tasks WHERE completed = 0")
fun getActiveTasks(): LiveData<List<Task>>
@Insert
suspend fun insertTask(task: Task)
}
// 在 ViewModel 中:
class TaskViewModel(application: Application) : AndroidViewModel(application) {
private val dao = AppDatabase.getDatabase(application).taskDao()
val activeTasks: LiveData<List<Task>> = dao.getActiveTasks()
}
Room 生成跟踪 tasks 表中更改的代码,并在任何 INSERT、UPDATE 或 DELETE 时自动更新 LiveData。这无需额外代码即可工作——仅需要带有 LiveData 返回类型的 @Query 注释。在 IT Sectr,我们从 2019 年开始在 Android 项目中使用 Room + LiveData 作为本地数据缓存的标准堆栈。
常见问题
LiveData — 具有内置 Lifecycle 支持的可观察容器:Observer 自动激活/停用。StateFlow — 来自 Kotlin Coroutines(Kotlinx Coroutines 1.3.7+)的反应流,不与 Lifecycle 绑定,但通过 stateIn(WhileSubscribed) 支持它。StateFlow 需要在 View 中显式管理生命周期,但提供对协程、Flow 操作符和跨平台的支持。Google 推荐在新的 Kotlin 项目中使用 StateFlow,而在需要与旧库兼容的 Java 代码中则推荐使用 LiveData。
使用 lifecycle-livedata-ktx 库中的扩展函数 liveData.asFlow()。它创建一个 Flow,在每次更改时发出 LiveData 的当前值。然后通过 .stateIn(viewModelScope, SharingStarted.WhileSubscribed(5000), initialValue) 转换为 StateFlow。反向转换——stateFlow.asLiveData()。相互转换允许在同一个项目中利用两个库的优势。
postValue() 使用 AtomicReference 存储延迟值。如果在主线程处理之前调用了两次 postValue(),第一个值将被第二个覆盖——Observable 只会收到最后一个值。这是因为 LiveData 没有内部队列:它只存储一个延迟值。要传递每个中间点(1%、2%、…100%),请在主线程上使用 setValue() 或使用来自 kotlinx-coroutines 的 ConflatedFlow。
可以,LiveData 可以通过 observeForever() 来观察,传递 Observer 而不需要 LifecycleOwner。但是,在这种情况下,必须通过 removeObserver() 显式取消订阅——自动取消订阅不起作用。observeForever() 在服务、ContentProvider 或 ViewModel 中使用,其中 LifecycleOwner 不可用。根据 Google 的建议,避免在 Activity/Fragment 中使用 observeForever()——使用带有 LifecycleOwner 的 observe()。
行为特点:当 LiveData 收到新的活动 Observer 时,它会立即收到最后一个值(如果已设置)。旧版本的 LiveData(在 lifecycle 2.5.0 之前)在过渡到活动状态时甚至向非活动订阅者传递值——这已修复。在当前版本中,LiveData 在从非活动状态过渡到活动状态时接收最后一个值,这简化了屏幕的初始化。
总结
我们将开发一款交钥匙移动应用程序
IT Sectr自2017年以来为初创企业和企业打造iOS和Android应用程序。我们将为您提供咨询并提出最佳解决方案。