一、从问题出发
场景:点赞数变了,界面要更新
假设有一个点赞按钮,后面有个数字显示点赞数:
class VideoActivity : Activity() {
var likeCount = 0
lateinit var likeTextView: TextView
fun onLikeButtonClick() {
likeCount = likeCount + 1
likeTextView.text = "$likeCount" // 数据变了,手动刷新界面
}
}看起来没问题。但实际场景中,点赞数可能从很多地方变化:
// 用户点了赞
fun onLikeButtonClick() {
likeCount = likeCount + 1
likeTextView.text = "$likeCount" // 要记得刷新
}
// 网络请求回来了,服务端告诉你真实数量
fun onNetworkResponse(count: Int) {
likeCount = count
likeTextView.text = "$likeCount" // 又要记得刷新
}
// 从其他页面返回,数据同步了
fun onSyncData(count: Int) {
likeCount = count
likeTextView.text = "$likeCount" // 还要记得刷新
}问题:每次改数据,都要手动刷新界面。漏了一处就是 bug。
二、LiveData 是什么
LiveData 的核心思路:你只管改数据,界面自动更新。
// LiveData 就是一个"可以被观察的数据容器"
val likeCount = MutableLiveData<Int>(0)
// ^^^^^^^^^^^^^^^^^^^^^^
// 不是普通的 Int,而是一个"可被观察的 Int"一句话定义:LiveData = 可观察的数据容器 + 自动感知生命周期。
三、LiveData 基本用法
分两步:
第一步:观察数据(一般在 onCreate 里做一次)
override fun onCreate() {
// 告诉 LiveData:"你的值变了就通知我"
likeCount.observe(this) { newValue ->
// 这个回调会在数据变化时自动执行
likeTextView.text = "$newValue"
}
}第二步:改数据(任何地方,任何时候)
// 用户点赞
fun onLikeButtonClick() {
likeCount.value = likeCount.value!! + 1 // 只管改数据
// 不用手动刷新界面,LiveData 会自动通知 observe 里的回调
}
// 网络返回
fun onNetworkResponse(count: Int) {
likeCount.value = count // 只管改数据
// 界面自动更新
}
// 数据同步
fun onSyncData(count: Int) {
likeCount.value = count // 只管改数据
// 界面自动更新
}不管从哪里改 likeCount.value,observe** 里注册的回调都会自动执行。**
四、对比:有 LiveData vs 没有 LiveData
没有 LiveData
var likeCount = 0
改数据的地方A: likeCount = 1; textView.text = "$likeCount" // 手动刷
改数据的地方B: likeCount = 2; textView.text = "$likeCount" // 手动刷
改数据的地方C: likeCount = 3; textView.text = "$likeCount" // 手动刷
(漏一处就是 bug)有 LiveData
val likeCount = MutableLiveData(0)
注册一次: likeCount.observe { textView.text = "$it" }
改数据的地方A: likeCount.value = 1 // 界面自动更新
改数据的地方B: likeCount.value = 2 // 界面自动更新
改数据的地方C: likeCount.value = 3 // 界面自动更新
(不可能漏)五、LiveData 的生命周期感知能力
这是 LiveData 相比普通回调/EventBus 的核心优势。
注意observe的第一个参数:
likeCount.observe(this) { newValue ->
// ^^^^
// this 就是 LifecycleOwner(Activity)
likeTextView.text = "$newValue"
}传了this(Activity)之后,LiveData 会自动判断:
| 页面状态 | LiveData 行为 |
|---|---|
| 页面可见(RESUMED) | 数据变了就通知 |
| 页面不可见(STOPPED) | 数据变了先不通知,等页面回来再通知 |
| 页面销毁(DESTROYED) | 自动取消观察,不会内存泄漏 |
具体流程示例
用户在看页面 → likeCount.value = 5 → 立刻回调,界面刷新
用户按了Home → likeCount.value = 8 → 不回调(页面不可见,刷了也没用)
用户切回来 → 自动用最新值 8 回调一次 → 界面刷新
用户关了页面 → 自动取消观察,不用手动清理六、Lifecycle Observer 回顾
LiveData 的生命周期感知能力,底层依赖的就是 Lifecycle + Observer 机制。
Lifecycle Observer 做的事:观察”页面状态”变化
Lifecycle + Observer → 观察"页面状态"变化
页面 onResume 了 → 通知我
页面 onPause 了 → 通知我LiveData 做的事:观察”数据”变化
LiveData + observe → 观察"数据"变化
点赞数变了 → 通知我
评论数变了 → 通知我
而且页面不可见时自动暂停通知LiveData 把 Lifecycle(页面状态感知)和数据观察结合在了一起。
七、MutableLiveData vs LiveData
| 类型 | 可读 | 可写 | 使用场景 |
|---|---|---|---|
LiveData<T> | getValue() | 不可写 | 对外暴露,只允许观察 |
MutableLiveData<T> | getValue() | setValue() / postValue() | 内部使用,允许修改 |
常见模式:ViewModel 内部用 MutableLiveData,对外暴露 LiveData。
class VideoViewModel : ViewModel() {
// 内部可写
private val _likeCount = MutableLiveData(0)
// 外部只读
val likeCount: LiveData<Int> = _likeCount
fun like() {
_likeCount.value = _likeCount.value!! + 1
}
}八、setValue() vs postValue()
| 方法 | 线程要求 | 说明 |
|---|---|---|
setValue(value) | 必须在主线程调用 | 同步设置,立即通知观察者 |
postValue(value) | 可以在任意线程调用 | 异步 post 到主线程后再通知 |
// 主线程中
likeCount.value = 5 // OK,用 setValue
// 子线程 / 网络回调中
likeCount.postValue(5) // OK,用 postValue九、总结
┌──────────────────────────────────────────────────┐
│ 传统做法 │
│ 数据变了 → 手动找到所有 UI → 逐个刷新 │
│ (容易遗漏,容易内存泄漏) │
└──────────────────────────────────────────────────┘
↓ 演进为
┌──────────────────────────────────────────────────┐
│ LiveData │
│ 数据变了 → 自动通知所有观察者 → UI 自动刷新 │
│ 页面不可见 → 自动暂停通知 │
│ 页面销毁 → 自动取消观察 │
│ (不会遗漏,不会泄漏) │
└──────────────────────────────────────────────────┘核心要点:
- LiveData 是可观察的数据容器 —— 数据变了自动通知
- 感知生命周期 —— 页面不可见不通知,页面销毁自动清理
- 数据驱动 UI —— 改数据就行,不用操心界面刷新
- 结合 ViewModel 使用 —— ViewModel 持有 LiveData,Activity/Fragment 观察它
更新: 2026-03-08 14:22:13
原文: https://www.yuque.com/dongpozhouzi-mshe3/zhm85g/swe9n9ygui5wf2l8