首页 / Android 入门教程 / LiveData

Android 入门教程

LiveData

本教程共 100 篇 · 第 79 篇 · 更新于 2026-07-28 · 约 7 分钟阅读

AndroidAndroid 入门教程LiveDataJetpackViewModel观察者模式Flow

79. LiveData

本节目标:理解 LiveData 的生命周期感知特性,会用 MutableLiveData 持有状态、map/switchMap 转换数据,知道 LiveData 和 StateFlow 怎么选。

LiveData 是 Jetpack 早期的可观察数据容器,自带生命周期感知。Compose 时代它被 StateFlow 抢了风头,但老代码、View 体系里还在用,得会。

LiveData 是什么

LiveData 是个「被观察的数据持有者」。和普通变量不同的是,它能让多个观察者订阅变化,并且只在观察者生命周期活跃时才推送更新。

打个比方,LiveData 像订阅的报纸,编辑部(ViewModel)一有新内容就送来。但如果你出门度假(Activity 不可见),送报纸的就先不送,等你回来(Activity 可见)再补上最近一期。

特点:

  • 生命周期感知:观察者处于 STARTEDRESUMED 才收到更新。
  • 自动解绑:观察者的 LifecycleOwner 销毁时自动移除订阅,不会泄漏。
  • 不可变 + 可变LiveData 只读,MutableLiveData 可写。
  • 主线程通知:更新都在主线程,UI 操作直接来。

基本用法

ViewModel 里持有 MutableLiveData,对外暴露 LiveData:

class NameViewModel : ViewModel() {
    private val _name = MutableLiveData<String>()
    val name: LiveData<String> = _name

    fun setName(newName: String) {
        _name.value = newName
    }
}

Activity 里观察:

class NameActivity : AppCompatActivity() {
    private val viewModel: NameViewModel by viewModels()

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContentView(R.layout.activity_name)

        viewModel.name.observe(this) { name ->
            nameTextView.text = name
        }

        button.setOnClickListener {
            viewModel.setName("张三")
        }
    }
}

要点:

  • observe(this)this(Activity 是 LifecycleOwner),自动绑生命周期。
  • _name.value = ... 在主线程改值,观察者立即收到。
  • 子线程改值要用 postValue()

setValue 和 postValue

// 主线程
_name.value = "张三"

// 子线程
_name.postValue("李四")

postValue 切到主线程再发,多个连续调用只保留最后一个。

Warning

子线程调 setValue 会崩。在协程里改 LiveData 要么 withContext(Dispatchers.Main),要么直接用 postValue

LiveData + Room

Room 的 DAO 能直接返回 LiveData:

@Dao
interface NoteDao {
    @Query("SELECT * FROM notes")
    fun getAll(): LiveData<List<Note>>
}

数据库一变,LiveData 自动推新值。但官方现在更推荐用 Flow,下面会讲。

LiveData + 协程

liveData {} 构建器让 LiveData 和协程配合:

val user: LiveData<User> = liveData {
    val data = repo.fetchUser()
    emit(data)
}

emit 推一次值。还能配合 emitSource 切换数据源:

val result: LiveData<Result> = liveData {
    emit(Result.Loading)
    try {
        val data = repo.fetch()
        emit(Result.Success(data))
    } catch (e: Exception) {
        emit(Result.Error(e))
    }
}

转换:map 和 switchMap

Transformations.map 把一个 LiveData 转成另一个:

val user: LiveData<User> = ...
val userName: LiveData<String> = user.map { "${it.firstName} ${it.lastName}" }

Transformations.switchMap 用于「上游值变了换一个新的 LiveData」:

val userId: MutableLiveData<String> = ...
val user: LiveData<User> = userId.switchMap { id ->
    repository.getUser(id)  // 返回 LiveData<User>
}

switchMap 适合「输入变了切换数据源」的场景,比如搜索关键词变了换一个查询。

MediatorLiveData 合并多源

MediatorLiveData 能观察多个 LiveData,任一变化都触发:

val mediator = MediatorLiveData<String>()

mediator.addSource(localData) { value ->
    mediator.value = "本地:$value"
}

mediator.addSource(remoteData) { value ->
    mediator.value = "网络:$value"
}

适合「本地 + 网络合并显示」这种场景。

LiveData vs StateFlow

现代 Android 开发 LiveData 和 StateFlow 怎么选?看这张表:

维度LiveDataStateFlow
生命周期感知内置需要 repeatOnLifecyclecollectAsStateWithLifecycle
默认值必须有
初始值第一次 setValue 才有构造时就要
操作符少(map/switchMap)多(Flow 全套)
线程主线程可配置
Compose 集成observeAsState()collectAsStateWithLifecycle()
数据层使用不推荐推荐
老项目兼容-

官方现在的建议:

  • ViewModel 到 UI 这一层:StateFlow 优先,LiveData 也可以。
  • 数据层(Repository、DataSource):用 Flow,不要用 LiveData。
  • Compose 项目:StateFlow 更顺手。
  • View/XML 老项目:LiveData 写起来简洁。
Note

LiveData 不适合数据层,原因是它的转换都在主线程跑,可能卡 UI。Flow 的 flowOn 能切线程,灵活得多。

Compose 里用 LiveData

Compose 也能观察 LiveData:

@Composable
fun NameScreen(viewModel: NameViewModel = viewModel()) {
    val name by viewModel.name.observeAsState(initial = "")
    Text("名字:$name")
}

observeAsState 来自 runtime-livedata 库。但新项目建议直接用 StateFlow + collectAsStateWithLifecycle

Flow 转 LiveData

老项目迁移时,Repository 返回 Flow,ViewModel 转 LiveData 暴露给 View:

class MyViewModel(repo: MyRepository) : ViewModel() {
    val data: LiveData<List<Item>> = repo.observeItems()
        .asLiveData(viewModelScope.coroutineContext)
}

或者:

val data: LiveData<List<Item>> = repo.observeItems().asLiveData()
Tip

迁移策略:数据层先换 Flow,ViewModel 暴露层可以暂时保持 LiveData 兼容老 UI,UI 改 Compose 时再换 StateFlow。

LiveData 的扩展

自定义 LiveData 可以重写 onActive()onInactive()

class LocationLiveData(private val ctx: Context) : LiveData<Location>() {
    private val manager = ctx.getSystemService(LocationManager::class.java)

    override fun onActive() {
        // 有观察者了,开始定位
        manager.requestLocationUpdates(listener)
    }

    override fun onInactive() {
        // 没观察者了,停止定位省电
        manager.removeUpdates(listener)
    }
}

onActive 在第一个观察者订阅时调用,onInactive 在最后一个观察者解绑时调用。适合「按需启动/停止」的资源。

常见坑

  1. Fragment 用 this 观察:Fragment 视图销毁后还在收,崩。用 viewLifecycleOwner
  2. 子线程调 setValue:崩。用 postValue
  3. LiveData 放 Repository:转换在主线程,可能卡 UI。用 Flow。
  4. LiveData 装一次性事件:旋转屏幕重复触发。用 SingleLiveEvent 或者 SharedFlow。
  5. observeForever 忘记 remove:泄漏。一般用 observe(lifecycleOwner)
  6. switchMap 漏写:直接调 repository.getUser(id) 返回 LiveData 但不订阅,没数据。

小结

LiveData 是生命周期感知的可观察数据容器,适合 ViewModel 到 UI 这一层。MutableLiveData 持有可变状态,map/switchMap 转换,MediatorLiveData 合并多源。数据层用 Flow,UI 层 LiveData 或 StateFlow 都行,新项目优先 StateFlow。

下一章讲 Lifecycle 组件,看怎么让普通类也能感知生命周期。