2. LiveData 基础:观察者模式、生命周期感知、数据更新

2.1 核心原理:生命周期感知的观察者包装

LiveData 是 Jetpack 中一个生命周期感知的可观察数据持有类。它的本质是 观察者模式 在 Android 组件生命周期上的增强实现:

  • 普通观察者:数据变化时无条件通知所有订阅者。
  • LiveData 观察者:只有当观察者(如 Activity/Fragment)处于 STARTED 或 RESUMED 状态时,才会发起通知;并且能自动在宿主销毁时移除观察者,避免内存泄漏。
关键原则: LiveData 自身不持有 Activity/Fragment 的强引用,它通过 LifecycleOwner 接口获取生命周期状态,并在状态发生改变时(如 ON_START、ON_STOP)自动调整通知策略。

2.2 体系架构图

下图展示了 LiveData 内部的三层关系:数据持有层、生命周期桥接层、观察者回调层。

LiveData 生命周期感知观察者机制 LiveData 对象 mData: T setValue(T) / postValue(T) LifecycleBridge LifecycleEventObserver onStateChanged() Observer onChanged(T?) 主动回调 LifecycleOwner Activity / Fragment 核心工作流程: ① 调用 observe(LifecycleOwner, Observer) 时,LiveData 将 Observer 包装为 LifecycleObserver。 ② 当 LifecycleOwner 进入 STARTED 状态后,LiveData 标记观察者为“活跃”。 ③ setValue/postValue 更新 mData,遍历所有活跃观察者并回调 onChanged。 ④ 在 ON_DESTROY 事件时自动移除观察者,防止内存泄漏和僵尸回调。

2.3 创建与观察

使用 LiveData 需要三步:创建、观察、更新。

2.3.1 创建 LiveData 对象

// 方式一:直接使用 MutableLiveData(可变,推荐)
val userNameLiveData = MutableLiveData<String>()

// 方式二:给初始值
val countLiveData = MutableLiveData<Int>(0)

// 方式三:转换 LiveData(不可变对外暴露)
val userName: LiveData<String> = userNameLiveData

2.3.2 在 Activity/Fragment 中观察

class MainActivity : AppCompatActivity() {
    private val viewModel: MainViewModel by viewModels()

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

        // 使用 lifecycleScope 或直接传入 this(LifecycleOwner)
        viewModel.userName.observe(this) { name ->
            // 仅当 Activity 处于 STARTED 或 RESUMED 状态时执行
            textView.text = name
        }
    }
}
✅ 最佳实践: 总是使用 observe(LifecycleOwner, Observer) 而不是 observeForever(Observer),后者不会自动解除注册,容易导致内存泄漏。
如果需要主动移除观察者,可以使用 removeObserver(Observer) 或 removeObservers(LifecycleOwner)。

2.4 数据更新方式

方法 线程 特点 使用场景
setValue(T) 主线程 立即同步更新,直接回调观察者 UI 事件、即时数据变更
postValue(T) 任意线程 异步提交到主线程队列,可能合并中间值 后台线程(如协程 Dispatchers.IO)计算结果后更新
// 主线程更新(setValue)
countLiveData.setValue(42)  // 立即通知观察者

// 后台线程更新(postValue)
viewModelScope.launch(Dispatchers.IO) {
    val result = fetchRemoteData()   // 耗时代码
    countLiveData.postValue(result)  // 异步到主线程后通知
}

2.5 生命周期感知的详细行为

当 LifecycleOwner 在 ON_STOP(如按 Home 键)时,LiveData 会将所有观察者标记为“非活跃”但不卸载;当重新进入 ON_START 或 ON_RESUME 时自动恢复活跃。在 ON_DESTROY 时自动移除所有观察者。

Lifecycle 事件 Activity 状态 观察者活跃状态 LiveData 行为
ON_CREATE Created 非活跃 不回调,但已注册
ON_START Started 活跃 如果数据有变化且之前未收到,立即回调一次(粘性事件特性)
ON_RESUME Resumed 活跃 正常接收变化通知
ON_PAUSE Paused 活跃(仍可收到通知) 仍可接收更新(Android 中 Paused 仍可见部分 UI)
ON_STOP Stopped 非活跃 暂停接收通知,数据缓存
ON_DESTROY Destroyed 自动移除 移除观察者引用,防止泄漏

2.6 常见错误与调试

  • 错误1:在非主线程调用 setValue —— 抛出 CalledFromWrongThreadException。解决方案:使用 postValue。
  • 错误2:忘记指定 LifecycleOwner —— 使用 observeForever 而不调用 removeObserver,导致内存泄漏。解决方案:始终使用 observe 并传入有效的 LifecycleOwner。
  • 错误3:多次调用 setValue 覆盖 issue —— 在粘性事件场景下,订阅者可能在活跃后收到之前的值。如果需要非粘性,考虑使用 EventWrapper 或 SingleLiveEvent。
⚠️ 调试技巧: LiveData 不会在 Logcat 中主动输出生命周期变化。可以在 Observer 的 onChanged 第一行打印当前线程和值,例如:
Log.d("LiveData", "onChanged: $it, thread: ${Thread.currentThread().name}")

2.7 性能与稳定性优化

  • 避免大对象频繁更新:LiveData 的 setValue 会触发序列化通知,若数据对象庞大(如 Bitmap、List > 1000 项),应考虑使用 postValue 或只在需要时更新。
  • 合并高频更新:在多线程频繁调用 postValue 时,中间值可能被丢弃,最终只保留最后一个值。这对 UI 抖动场景反而是利好——减少不必要的重绘。
  • 使用 Transformations.map / switchMap:避免手写 MediatorLiveData 管理多个 LiveData,让框架自动处理生命周期感知的绑定。
  • 内存泄漏检测:在 LeakCanary 中如果发现 ViewModel 或 Activity 泄漏,优先检查是否存在 observeForever 或未移除的 Observer。

2.8 单元测试 LiveData

为了在测试中触发 LiveData 的活跃回调,需要手动设置 LifecycleOwner 的状态:

class LiveDataTest {

    private lateinit var liveData: MutableLiveData<String>
    private lateinit var lifecycleOwner: LifecycleOwner
    private lateinit var lifecycle: LifecycleRegistry

    @Before
    fun setup() {
        liveData = MutableLiveData()
        lifecycleOwner = TestLifecycleOwner()
        lifecycle = LifecycleRegistry(lifecycleOwner)
        lifecycle.handleLifecycleEvent(Lifecycle.Event.ON_CREATE)
        lifecycle.handleLifecycleEvent(Lifecycle.Event.ON_START)  // 标记为活跃
    }

    @Test
    fun `observe receives value when active`() {
        val values = mutableListOf<String>()
        liveData.observe(lifecycleOwner) { values.add(it) }

        liveData.value = "Hello"
        assertEquals(listOf("Hello"), values)
    }

    @After
    fun tearDown() {
        lifecycle.handleLifecycleEvent(Lifecycle.Event.ON_DESTROY)  // 清理
    }
}

internal class TestLifecycleOwner : LifecycleOwner {
    override val lifecycle: Lifecycle = LifecycleRegistry(this)
}

更多测试辅助可以使用 InstantTaskExecutorRule 让 LiveData 在主线程同步执行:

@RunWith(JUnit4::class)
class LiveDataTestWithRule {

    @get:Rule
    val instantRule = InstantTaskExecutorRule()

    // 之后所有 LiveData 操作都在调用线程立即执行,便于断言
}