一、背景:为什么我们需要 repeatOnLifecycle?
在传统 Fragment 懒加载中,我们常这样写:
override fun onResume() {
super.onResume()
requestData()
}
或者:
lifecycleScope.launchWhenStarted {
flow.collect { render(it) }
}
看似正常,但在真实项目中会遇到:
-
页面多次进入重复请求
-
Fragment View 销毁后仍在收集
-
协程未正确取消导致泄漏
-
接口被重复调用
-
UI 渲染错乱
在复杂业务场景下,这些问题会直接影响稳定性。
从 Jetpack Lifecycle 2.4.0 开始,官方推荐使用:
repeatOnLifecycle
二、repeatOnLifecycle 本质是什么?
一句话总结:
它是一个“生命周期驱动的协程取消 + 重建机制”。
函数签名:
public suspend fun Lifecycle.repeatOnLifecycle(
state: Lifecycle.State,
block: suspend CoroutineScope.() -> Unit
)
关键点:
- 它是 suspend 函数
- block 自带 CoroutineScope
- 生命周期降级时取消协程
- 生命周期升级时重新执行 block
三、为什么必须放在 launch 里?
因为它是 suspend 函数:
viewLifecycleOwner.lifecycleScope.launch {
viewLifecycleOwner.repeatOnLifecycle(Lifecycle.State.STARTED) {
flow.collect { render(it) }
}
}
不能直接调用:
repeatOnLifecycle { } // 编译错误
四、底层原理(核心)
repeatOnLifecycle 内部做了 4 件事:
1️⃣ 注册 LifecycleObserver
2️⃣ 生命周期 ≥ 目标状态 → 启动子协程
3️⃣ 生命周期 < 目标状态 → 取消子协程
4️⃣ 使用 coroutineScope 保证结构化并发
简化模型:
coroutineScope {
val observer = LifecycleEventObserver { _, _ ->
if (currentState >= targetState) {
launch { block() }
} else {
childJob?.cancel()
}
}
}
它不是挂起恢复模型,而是:
取消 + 重建模型
这比 launchWhenStarted 更安全。
五、它和 launchWhenStarted 的区别
| launchWhenStarted | repeatOnLifecycle | |
|---|---|---|
| 生命周期变化 | 挂起 | 取消 |
| 再进入 | 从挂起点继续 | 重新执行 block |
| 子协程管理 | 不严格 | 结构化并发 |
| 官方推荐 | ❌ | ✅ |
核心差异:
launchWhenX 是“暂停恢复”
repeatOnLifecycle 是“取消重建”
取消比挂起更安全,因为子协程会被彻底销毁,不用担心恢复时生命周期view销毁引发的异常崩溃。
六、错误示例:不要在里面直接写网络请求
错误写法:
repeatOnLifecycle(STARTED) {
val data = repository.request()
render(data)
}
问题:
-
每次回到 STARTED 都会重新请求
-
切后台再回来 → 再请求
-
旋转屏幕 → 再请求
repeatOnLifecycle 负责的是“安全收集”,
不应该承担“业务触发”。
七、正确结构:用 repeatOnLifecycle 替代 onResume 懒加载
我们最开始的问题是:
override fun onResume() {
super.onResume()
requestData()
}
问题在于:
- View 销毁后仍可能继续执行
- 容易重复请求
- 与协程结构不匹配
- 不符合现代 UDF 架构
✅ 正确做法:ViewModel 负责请求
class MyViewModel : ViewModel() {
private val _data = MutableStateFlow<Data?>(null)
val data: StateFlow<Data?> = _data
fun load() {
viewModelScope.launch {
_data.value = repository.request()
}
}
}
说明:
- 网络请求在 ViewModel
- 数据通过 StateFlow 暴露
- UI 不直接持有业务逻辑
✅ Fragment:用 repeatOnLifecycle 替代 onResume
class MyFragment : Fragment(R.layout.fragment_my) {
private val viewModel: MyViewModel by viewModels()
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
viewLifecycleOwner.lifecycleScope.launch {
viewLifecycleOwner.repeatOnLifecycle(Lifecycle.State.STARTED) {
// 等价于 onResume 的懒加载行为
viewModel.load()
// 生命周期安全收集数据
viewModel.data.collect { data ->
render(data)
}
}
}
}
private fun render(data: Data?) {
// 更新 UI
}
}
为什么这等价于 onResume?
因为:
-
Fragment 可见时生命周期 ≥ STARTED
-
切后台时生命周期 < STARTED
-
再回来时重新进入 STARTED
而 repeatOnLifecycle 的机制是:
每次进入 STARTED 都重新执行 block
因此:
viewModel.load()
会在以下场景执行:
-
页面首次显示
-
切后台再回来
-
从回退栈返回
-
屏幕旋转重建
这就是“安全版 onResume”。
如果只想首次加载一次怎么办?
那就不要把 load() 放在 repeatOnLifecycle 里:
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
// 只执行一次
viewModel.load()
viewLifecycleOwner.lifecycleScope.launch {
viewLifecycleOwner.repeatOnLifecycle(Lifecycle.State.STARTED) {
viewModel.data.collect { render(it) }
}
}
}
| 需求 | 写法 |
|---|---|
| 每次页面可见刷新(等价 onResume) | 在 repeatOnLifecycle 里调用 load() |
| 只首次加载一次 | 在 onViewCreated 里调用 load() |
这样结构非常清晰:
-
repeatOnLifecycle 负责生命周期安全
-
ViewModel 负责请求
-
UI 负责收集
-
不再使用 onResume
-
不再使用 launchWhenX
逻辑干净、边界清晰。
八、为什么它和 Flow 天然契合?
Flow 是“数据流”,不是“任务”。
StateFlow 特性:
-
永远持有最新值
-
可重复订阅
-
生命周期重新 START 时会立即发出当前值
当 STOP:
- collect 被取消
当重新 START:
- 重新 collect
- 立即拿到最新状态
- 不会重复请求(因为请求不在 UI 层)
九、Fragment 中的重要细节
错误写法:
lifecycleScope.launch {
repeatOnLifecycle(STARTED) { }
}
正确写法:
viewLifecycleOwner.lifecycleScope.launch {
viewLifecycleOwner.repeatOnLifecycle(STARTED) { }
}
原因:
Fragment 生命周期 ≠ View 生命周期。
当 Fragment 进入回退栈:
-
onDestroyView 执行(View 销毁)
-
Fragment 本身仍是 STARTED
如果使用 lifecycleScope,协程仍然运行,访问 View 会 Crash。
必须使用 viewLifecycleOwner。
十、自定义 LifecycleOwner(进阶)
只要实现 LifecycleOwner,就可以使用 repeatOnLifecycle。
这意味着:
repeatOnLifecycle 并不依赖 Fragment 或 Activity。
1️⃣ 自定义 LifecycleOwner
class MyLifecycleOwner : LifecycleOwner {
private val registry = LifecycleRegistry(this)
override fun getLifecycle(): Lifecycle = registry
fun onCreate() {
registry.handleLifecycleEvent(Lifecycle.Event.ON_CREATE)
}
fun onStart() {
registry.handleLifecycleEvent(Lifecycle.Event.ON_START)
}
fun onResume() {
registry.handleLifecycleEvent(Lifecycle.Event.ON_RESUME)
}
fun onPause() {
registry.handleLifecycleEvent(Lifecycle.Event.ON_PAUSE)
}
fun onStop() {
registry.handleLifecycleEvent(Lifecycle.Event.ON_STOP)
}
fun onDestroy() {
registry.handleLifecycleEvent(Lifecycle.Event.ON_DESTROY)
}
}
这里我们手动驱动生命周期。
2️⃣ 在普通类中使用 repeatOnLifecycle
class TestComponent {
private val lifecycleOwner = MyLifecycleOwner()
private val scope = CoroutineScope(SupervisorJob() + Dispatchers.Main)
fun start() {
scope.launch {
lifecycleOwner.repeatOnLifecycle(Lifecycle.State.STARTED) {
println("进入 STARTED,开始执行任务")
try {
while (true) {
delay(1000)
println("任务执行中...")
}
} finally {
println("生命周期降级,任务被取消")
}
}
}
// 手动驱动生命周期
lifecycleOwner.onCreate()
lifecycleOwner.onStart()
}
fun stop() {
lifecycleOwner.onStop()
}
fun destroy() {
lifecycleOwner.onDestroy()
scope.cancel()
}
}
3️⃣ 运行逻辑说明
当执行:
val component = TestComponent()
component.start()
输出:
进入 STARTED,开始执行任务
任务执行中...
任务执行中...
当执行:
component.stop()
输出:
生命周期降级,任务被取消
再次执行:
component.start()
任务会重新启动。
这块有些同学可能会困惑。为什么调用 onStop()(ON_STOP 事件)会让 STARTED → CREATED。
先记住Android 生命周期状态的层级(从低到高)
核心规则:每个事件(Event)都会固定把状态切换到对应的层级,且状态只能 “降级” 或 “升级”,不能跳级。
LifecycleRegistry 收到 ON_STOP 事件后,会按照 Android 内置的规则,把当前状态从 STARTED(对应 ON_START/ON_PAUSE)强制切换到 CREATED(因为 ON_STOP 事件的唯一对应状态就是 CREATED)。
repeatOnLifecycle 的核心机制:当生命周期状态低于指定的 State(这里是 STARTED)时,会自动取消当前协程
协程被取消时,delay(1000)这个挂起函数会抛出 CancellationException,触发 try 块的中断,进而执行 finally 块里的代码。但是这个异常会被repeatOnLifecycle捕获,不会触发崩溃。
类似能抛出CancellationException的挂起函数还包括Flow.collect(),withContext()/async(),网络请求、数据库查询等封装的挂起函数(如 Retrofit 的 suspend 接口)。
而普通的非挂起函数(比如 println()、for 循环)不会主动检测取消状态,此时即使协程被取消,Thread.sleep 也不会抛异常,while 循环会一直执行,finally 块永远不会触发 —— 这也是为什么协程取消是 “协作式” 的,必须通过可取消挂起函数配合。
这个例子说明什么?
说明:
- repeatOnLifecycle 本质只是监听 Lifecycle
- 它和 Fragment 无关
- 只要有 LifecycleOwner 就可以使用
- 生命周期变化会自动取消并重建协程
实际应用场景
这种模式常用于:
- 自定义 View 容器
- 播放器组件
- SDK 封装
- 跨端桥接
- 非 Activity / Fragment 场景
十一、总结
repeatOnLifecycle 优化的不是 CPU 性能。
它优化的是:
-
生命周期安全
-
协程结构管理
-
重复订阅控制
-
稳定性
它代表的是:
-
取消重建模型
-
结构化并发
-
单向数据流
真正理解 repeatOnLifecycle,
才算真正理解现代 Android 生命周期与协程协作模型。