LiveData:什么是它,Android Architecture 组件

作者: IT Sectr 发布日期: 2026-02-20 阅读时间: 8 分钟

LiveData — 来自 Android Jetpack 的可观察数据容器,它考虑了 Activity、Fragment 或 Service 的生命周期。让我们看看 LiveData 如何自动管理订阅:活跃的订阅者获得更新,而不活跃的则不会,从而消除内存泄漏和因过时引用导致的崩溃。根据 Google(Android Developers,2025)的数据,LiveData 在 74% 的 Java 和 Kotlin 项目中被用作从 ViewModel 到 UI 的反应式数据传输的主要方式。

要点

  • LiveData — 考虑生命周期的可观察数据持有者:订阅者不活跃时自动取消订阅。
  • MutableLiveData — LiveData 的可变版本,具有 setValue()(主线程)和 postValue()(后台线程)方法。
  • Observer — 在 LifecycleOwner 处于活动状态时,获取数据更新通知的接口。
  • map()switchMap() 转换 — 无需创建新类即可进行 LiveData 转换的函数链。
  • MediatorLiveData — 将多个 LiveData 源合并为一个具有优先级管理的流。

Android 中的 LiveData 是什么?

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 的关键区别于其他可观察容器——与 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:setValue vs postValue

MutableLiveData — LiveData 的子类,具有用于更改存储值的开放方法 setValue()postValue()。与 LiveData 不同,MutableLiveData 可写,但在 ViewModel 中通常只发布 LiveData(不可变版本),将 MutableLiveData 隐藏在 private 修饰符下。

kotlin
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()

LiveData 转换:map、switchMap、MediatorLiveData

Transformations.map() — 将一个 LiveData 的值函数式转换为另一种类型,无需编写 Observer。例如,从 LiveData<User> 获取包含用户名的 LiveData<String>。转换是惰性的:仅在目标 LiveData 上有活动 Observer 时才执行转换。

kotlin
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 与协程:liveData builder

liveData { } — coroutine builder(出现在 lifecycle-livedata-ktx 2.2.0 中),允许在协程内异步计算 LiveData 的值。在 liveData { } 块内,可以使用 suspend 上下文以及用于发布值的 emit() 函数。当所有观察者都不活跃时,在 builder 中启动的所有协程都会自动取消。

kotlin
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% 的样板代码。

代码示例:Kotlin 中的 LiveData

示例 1:用于登录屏幕的 ViewModel 与 LiveData

经典的登录屏幕,包含电子邮件和密码字段、验证和加载状态。ViewModel 管理三个 LiveData:email、password 和 loginResult。

kotlin
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)
            }
        }
    }
}

示例 2:LiveData 与 Room 和协程

Room 支持 LiveData 作为 DAO 查询的返回类型:每次表更改时,LiveData 都会自动通知观察者,这对于反应式 UI 是理想的。

kotlin
@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 和 StateFlow 有什么区别?

LiveData — 具有内置 Lifecycle 支持的可观察容器:Observer 自动激活/停用。StateFlow — 来自 Kotlin Coroutines(Kotlinx Coroutines 1.3.7+)的反应流,不与 Lifecycle 绑定,但通过 stateIn(WhileSubscribed) 支持它。StateFlow 需要在 View 中显式管理生命周期,但提供对协程、Flow 操作符和跨平台的支持。Google 推荐在新的 Kotlin 项目中使用 StateFlow,而在需要与旧库兼容的 Java 代码中则推荐使用 LiveData。

如何将 LiveData 转换为 StateFlow?

使用 lifecycle-livedata-ktx 库中的扩展函数 liveData.asFlow()。它创建一个 Flow,在每次更改时发出 LiveData 的当前值。然后通过 .stateIn(viewModelScope, SharingStarted.WhileSubscribed(5000), initialValue) 转换为 StateFlow。反向转换——stateFlow.asLiveData()。相互转换允许在同一个项目中利用两个库的优势。

LiveData 何时在 postValue 时丢失数据?

postValue() 使用 AtomicReference 存储延迟值。如果在主线程处理之前调用了两次 postValue(),第一个值将被第二个覆盖——Observable 只会收到最后一个值。这是因为 LiveData 没有内部队列:它只存储一个延迟值。要传递每个中间点(1%、2%、…100%),请在主线程上使用 setValue() 或使用来自 kotlinx-coroutines 的 ConflatedFlow

可以在没有 LifecycleOwner 的情况下使用 LiveData 吗?

可以,LiveData 可以通过 observeForever() 来观察,传递 Observer 而不需要 LifecycleOwner。但是,在这种情况下,必须通过 removeObserver() 显式取消订阅——自动取消订阅不起作用。observeForever() 在服务、ContentProvider 或 ViewModel 中使用,其中 LifecycleOwner 不可用。根据 Google 的建议,避免在 Activity/Fragment 中使用 observeForever()——使用带有 LifecycleOwner 的 observe()。

什么是 LiveData 1.0 版本(始终相关)?

行为特点:当 LiveData 收到新的活动 Observer 时,它会立即收到最后一个值(如果已设置)。旧版本的 LiveData(在 lifecycle 2.5.0 之前)在过渡到活动状态时甚至向非活动订阅者传递值——这已修复。在当前版本中,LiveData 在从非活动状态过渡到活动状态时接收最后一个值,这简化了屏幕的初始化。

总结

  • LiveData — 带有自动 Lifecycle 绑定的可观察数据持有者,消除内存泄漏和因过时引用导致的崩溃。
  • MutableLiveData 带有 setValue()(主线程)和 postValue()(后台线程)——更改数据的主要 API。
  • map()switchMap()MediatorLiveData 转换——无需样板代码的函数链。
  • liveData builder liveData { }——创建带有自动协程取消的异步 LiveData 的协程方法。
  • Room + LiveData——无需 DAO 中额外代码即可进行本地缓存的现成连接。
  • LiveData 在 74% 的 Jetpack 项目中使用,并且仍然是 Java 代码和遗留架构的标准。
  • 对于新的 Kotlin 项目,Google 推荐 StateFlow,但 LiveData 仍然是混合堆栈的兼容解决方案。

我们将开发一款交钥匙移动应用程序

IT Sectr自2017年以来为初创企业和企业打造iOS和Android应用程序。我们将为您提供咨询并提出最佳解决方案。

讨论项目

另请阅读