Android 构建可管理生命周期的应用(二)

在前面第一篇有聊过

  • LifecycleOwner 是一个接口,表示某个类拥有一个 Lifecycle

  • LifecycleRegistryLifecycle 类的一个具体实现,它提供了管理生命周期状态的方法

  • 还举例传统分式中 ViewModelLiveData 如何跟随 FragmentActivity 中的Lifecycle生命周期工作的

今天我们聊,在现代应用中,另一个组件协程,如何如何跟随 FragmentActivity 中的Lifecycle生命周期工作的

先来看一下典型的例子

kotlin 复制代码
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
    viewLifecycleOwner.lifecycleScope.launch {
        repeatOnLifecycle(Lifecycle.State.STARTED) {
            someFlow.collect {
                // 更新 UI
            }
        }
    }
}

背后逻辑就是:

  • 页面可见且处于前台时(STARTED)才开始 collect
  • 页面不可见(变为 STOPPED)时自动取消 collect
  • 页面销毁时自动清理资源

lifecycleScope

kotlin 复制代码
public interface LifecycleOwner {
public val lifecycle: Lifecycle
}

public val LifecycleOwner.lifecycleScope: LifecycleCoroutineScope
    get() = lifecycle.coroutineScope

跟传统方式比较,就是扩展了一个属性*lifecycleScope*

内部是调用 lifecycle.coroutineScope

简单讲一下kotlin 扩展属性,lifecycle.coroutineScope **这段代码可以理解成 lifecycle 该类型的一个对象,访问了它的coroutineScope属性。下面继续看一下源码实现逻辑。

kotlin 复制代码
public val Lifecycle.coroutineScope: LifecycleCoroutineScope
    get() {
        while (true) {
            val existing = internalScopeRef.get() as LifecycleCoroutineScopeImpl?
            if (existing != null) {
                return existing
            }
            val newScope =
                LifecycleCoroutineScopeImpl(this, SupervisorJob() + Dispatchers.Main.immediate)
            if (internalScopeRef.compareAndSet(null, newScope)) {
                newScope.register()
                return newScope
            }
        }
    }

这个协程作用域内部交给了LifecycleCoroutineScopeImpl 它去实现了

kotlin 复制代码
internal class LifecycleCoroutineScopeImpl(
    override val lifecycle: Lifecycle,
    override val coroutineContext: CoroutineContext
) : LifecycleCoroutineScope(), LifecycleEventObserver {
    init {
        if (lifecycle.currentState == Lifecycle.State.DESTROYED) {
            coroutineContext.cancel()
        }
    }

    fun register() {
        launch(Dispatchers.Main.immediate) {
if (lifecycle.currentState >= Lifecycle.State.INITIALIZED) {
                lifecycle.addObserver(this@LifecycleCoroutineScopeImpl)
            } else {
                coroutineContext.cancel()
            }
        }
}

    override fun onStateChanged(source: LifecycleOwner, event: Lifecycle.Event) {
        if (lifecycle.currentState <= Lifecycle.State.DESTROYED) {
            lifecycle.removeObserver(this)
            coroutineContext.cancel()
        }
    }
}

内部先是注册了生命周期

lifecycle.addObserver(this@LifecycleCoroutineScopeImpl)

然后监听生命周期的变化onStateChanged 在页面销毁的时候,解除绑定,协程取消。

lifecycle.currentState <= Lifecycle.State.DESTROYED

所以最外层的协程viewLifecycleOwner.lifecycleScope.launch的生命周期结束是在 Lifecycle.State.DESTROYED 也就是fragment销毁之后

repeatOnLifecycle

kotlin 复制代码
public suspend fun LifecycleOwner.repeatOnLifecycle(
    state: Lifecycle.State,
    block: suspend CoroutineScope.() -> Unit
): Unit = lifecycle.repeatOnLifecycle(state, block)

继续看一下第二次的协程方法,这是一个拓展函数,一样是来自LifecycleOwner,内部实现依旧是交给lifecyclelifecycle.repeatOnLifecycle也是一个拓展函数,分析一下源码实现逻辑

kotlin 复制代码
public suspend fun Lifecycle.repeatOnLifecycle(
    state: Lifecycle.State,
    block: suspend CoroutineScope.() -> Unit
) {
    ...
    coroutineScope {
withContext(Dispatchers.Main.immediate) {
            if (currentState === Lifecycle.State.DESTROYED) return@withContext
            var launchedJob: Job? = null
            var observer: LifecycleEventObserver? = null
            try {
                suspendCancellableCoroutine<Unit> { cont ->
                    val startWorkEvent = Lifecycle.Event.upTo(state)
                    val cancelWorkEvent = Lifecycle.Event.downFrom(state)
                    val mutex = Mutex()
                    observer = LifecycleEventObserver { _, event ->
if (event == startWorkEvent) {
                            launchedJob =
                                this@coroutineScope.launch {
                                    mutex.withLock { coroutineScope { block() } }
}
return@LifecycleEventObserver
                        }
                        if (event == cancelWorkEvent) {
                            launchedJob?.cancel()
                            launchedJob = null
                        }
                        if (event == Lifecycle.Event.ON_DESTROY) {
                            cont.resume(Unit)
                        }
                    }
this@repeatOnLifecycle.addObserver(observer as LifecycleEventObserver)
                }
} finally {
                launchedJob?.cancel()
                observer?.let { this@repeatOnLifecycle.removeObserver(it) }
}
        }
}
}

suspendCancellableCoroutine 这是一个挂起函数,它需要我们手动去恢复 cont.resume (Unit)就是恢复逻辑,内部实现是注册了生命周期观察者 addObserver(observer as LifecycleEventObserver)

继续把代码简化一下,看看LifecycleEventObserver生命周期变化相关逻辑,

如果我们设置的是repeatOnLifecycle(Lifecycle.State.STARTED)

ini 复制代码
// LifecycleEventObserver
if (STARTED == startWorkEvent) {
    launchedJob =
        this@coroutineScope.launch {
            mutex.withLock { coroutineScope { block() } }
}
return@LifecycleEventObserver
}
if (ON_STOP == cancelWorkEvent) {
    launchedJob?.cancel()
    launchedJob = null
}
if (event == Lifecycle.Event.ON_DESTROY) {
    cont.resume(Unit)
}

也就是页面可见的时候,执行协成代码块,也就是

arduino 复制代码
someFlow.collect {
    // 更新 UI
}

页面不可见的时候,取消协程代码块执行

页面销毁的时候*ON_DESTROY,手动恢复协程cont. resume*(Unit),相当于协程结束了

所以,someFlow.collect更新UI的逻辑,在*ON_STOP会被取消了,跟最外层的协程逻辑是有差异的,最外层是更长的生命周期,它持续到 ON_DESTROY*

repeatOnLifecycle(Lifecycle.State.STARTED)很适合UI更新

相关推荐
松仔log5 小时前
Java中级——组合和继承
android·java·开发语言
我命由我123458 小时前
Jetpack Compose - MaterialExpressiveTheme 与 MaterialTheme、ColorScheme
android·java·开发语言·java-ee·kotlin·android jetpack·android runtime
lilian2339 小时前
Harmony os 技术实战|拼豆制图06:收藏 ID、生成记录与重启恢复怎么不打架
android·java·数据库·harmonyos
2601_955759629 小时前
ClaudeAPI成本中心与业务标签设计指南
android·java·数据库
余衫马9 小时前
2026最新版!Android Studio 极速安装与配置指南
android·ide·android studio
AFinalStone9 小时前
Android 7系统异常问题排查(五)Framework层(下)—System Server崩溃
android·tombstone·系统异常
AFinalStone9 小时前
Android 7系统异常问题排查(八)系统追踪—Trace机制与性能诊断
android·系统异常
极客猴子13 小时前
Android录音转写频繁卡顿?多款APP长时间会议场景稳定性实测
android·人工智能·智能手机·飞书
Kapaseker13 小时前
Boolean 变量到底该怎么命名?
android·kotlin
AFinalStone13 小时前
Android 7系统异常问题排查(六)应用异常(上)—ANR机制全解
android·系统异常