Lifecycle、ViewModel 与 StateFlow
核心原理讲解
Lifecycle 观察者模式是怎么实现的?
核心实现依托于三个角色:LifecycleOwner(生命周期持有者,如 Activity/Fragment)、LifecycleObserver(观察者)和 LifecycleRegistry(分发注册表)。

- 如何同步状态:
LifecycleRegistry内部维护了一个状态机(State 状态与 Event 事件)。当宿主生命周期变化时(比如 Activity 走到了onStart),会通过关联的ReportFragment向LifecycleRegistry发送一个ON_START的Event。 LifecycleRegistry接收到 Event 后,会推进自身的State(例如从CREATED变为STARTED),并遍历持有的观察者列表,逐个触发回调。它能保证不管观察者是在什么时候注册进来的,都能自动向后对齐到宿主当前的最新状态。
ViewModel 重建保活的机制
ViewModel 为什么能在屏幕旋转、语言切换等配置重建(Configuration Changes)后依然存活?

- 核心秘密:系统在销毁因配置变更重建的 Activity 时,会先调用
retainNonConfigurationInstances()将内部的ViewModelStore存起来。 - 重建恢复:当新 Activity 实例重建并创建
ViewModelProvider时,会通过getLastNonConfigurationInstance()拿回刚才暂存的ViewModelStore,从中取出原本就存在的 ViewModel 实例。 - 真正销毁:只有当 Activity 真正调用了
finish()退出生命周期(或者 Fragment 真正被 Pop 出栈)时,宿主才会告诉ViewModelStore调用所有 ViewModel 的onCleared()来彻底释放资源。
StateFlow 在 MVI 中的订阅防漏与生命周期感知
在 MVI 中,View 订阅 ViewModel 中的单一不可变状态 StateFlow。
如果直接在 lifecycleScope.launch 里收集(collect)数据,当 App 被切到后台(处于 STOPPED 状态)时,底层的 Flow 收集协程依然在活跃,生产者如果继续推送数据,协程依然会处理,这会造成不必要的 CPU 开销和潜在的 UI 异常。

- 解决办法:使用
repeatOnLifecycle(Lifecycle.State.STARTED)。它是一个挂起函数,当生命周期进入STARTED时自动启动协程进行 Flow 收集;一旦生命周期低于STARTED(比如切到后台变成STOPPED),它会直接取消(cancel)收集协程;回到前台后又会重新创建协程开始收集。这比旧的launchWhenStarted更加安全和省电。
工程注意事项
-
[!IMPORTANT] Fragment 生命周期订阅的坑(致命高频):在 Fragment 中观察 LiveData 或收集 StateFlow 时,
lifecycleOwner参数传入this还是viewLifecycleOwner?必须用viewLifecycleOwner!因为 Fragment 的视图销毁时(onDestroyView),Fragment 实例可能还活着(没有onDestroy),如果用了this,此时 LiveData 观察者没有被销毁,当数据变化时会尝试去操作已经销毁的 View 导致直接崩溃。viewLifecycleOwner的生命周期与 Fragment 的 View 绑定,在onDestroyView时会自动断开观察。 -
[!NOTE] MVI 状态 copy 的开销:MVI 强调 State 不可变,状态变化必须
copy整个对象。如果 UI 很复杂,高频变化时会有大量的对象创建和 GC 压力。需要在 ViewModel 中合理分层,利用 Flow 的distinctUntilChanged()对状态字段进行去重,防止不相关的子 View 频繁被动刷新。