背景
- 在repeatOnLifecycle还没出现的时候,相信大家用协程过程中多多少少都接触过 Activity/Fragment 的launchWhenXXXX(launchWhenResumed),随着kotlin版本不断迭代,现在新版本出现一个repeatOnLifecycle来替代launchWhenXXXX的使用,接下来让我们逐步分析它们两者的区别。
两者函数实现区别
launchWhenResumed分析
- launchWhenXXXX方法最后都会走到Lifecycle.whenStateAtLeast方法里面,下面我们直接从Lifecycle.whenStateAtLeast分析。
public fun launchWhenResumed(block: suspend CoroutineScope.() -> Unit): Job = launch {
lifecycle.whenResumed(block)
}
whenStateAtLeast函数解析
- 从下面可以看出创建dispatcher(任务分发器,主要是存储任务和管理内部状态)和 job(父Job,关联当前block)和controller(任务管理器)。
- withContext(dispatcher, block) 阻塞式将block回调塞给dispatcher,让controller在符合对应场景下将block进行回调。
public suspend fun <T> Lifecycle.whenStateAtLeast(
minState: Lifecycle.State,
block: suspend CoroutineScope.() -> T
): T = withContext(Dispatchers.Main.immediate) {
val job = coroutineContext[Job] ?: error("when[State] methods should have a parent job")
val dispatcher = PausingDispatcher()
val controller =
LifecycleController(this@whenStateAtLeast, minState, dispatcher.dispatchQueue, job)
try {
withContext(dispatcher, block)
} finally {
controller.finish()
}
}
LifecycleController类解析
- init方法可以看出在非Destroy状态下,添加一个observer,用于观察Activity/Fragment的生命周期变化后进行回调。
- observer负责内容:
<一> 当处于Destroy状态下停止parentJob作用域,停止、清空dispatcher队列待执行Job。
<二> 当Activity/Fragment 生命周期状态小于minState(这里对应是Resumed)时候,dispatcher队列暂停全部Job调度。
<三> 当Activity/Fragment 生命周期状态大于等于minState(这里对应是Resumed)时候,dispatcher队列恢复全部Job调度。
@MainThread
internal class LifecycleController(
……
) {
private val observer = LifecycleEventObserver { source, _ ->
if (source.lifecycle.currentState == Lifecycle.State.DESTROYED) {
// cancel job before resuming remaining coroutines so that they run in cancelled
// state
handleDestroy(parentJob)
} else if (source.lifecycle.currentState < minState) {
dispatchQueue.pause()
} else {
dispatchQueue.resume()
}
}
init {
// If Lifecycle is already destroyed (e.g. developer leaked the lifecycle), we won't get
// an event callback so we need to check for it before registering
// see: b/128749497 for details.
if (lifecycle.currentState == Lifecycle.State.DESTROYED) {
handleDestroy(parentJob)
} else {
lifecycle.addObserver(observer)
}
}
@Suppress("NOTHING_TO_INLINE") // avoid unnecessary method
private inline fun handleDestroy(parentJob: Job) {
parentJob.cancel()
finish()
}
/**
* Removes the observer and also marks the [DispatchQueue] as finished so that any remaining
* runnables can be executed.
*/
@MainThread
fun finish() {
lifecycle.removeObserver(observer)
dispatchQueue.finish()
}
}
launchWhenResumed总结
- launchWhenResumed是在Activity/Fragment 在生命周期OnResume就会执行对应Suspend函数,并且执行后的函数作用域会一直存在,直到Activity/Fragment被销毁为止。
- 如果flow+collect 在launchWhenResumed作用域下执行,那么collect首次生效后就会一直持续下去,当flow生成新的数据时候,Activity/Fragment就算生命周期比Resume状态大的前提下,collect也会收到新的数据回调。
repeatOnLifecycle分析
- 从repeatOnLifecycle函数可以看出最后统一走repeatOnLifecycle去处理不同State的情况。
public suspend fun LifecycleOwner.repeatOnLifecycle(
state: Lifecycle.State,
block: suspend CoroutineScope.() -> Unit
): Unit = lifecycle.repeatOnLifecycle(state, block)
repeatOnLifecycle(State, Block)函数
主要看以下几个点:
<一> addObserver,添加对应Activity/Fragment生命周期监听,以便在回到对应状态时创建新的作用域(launchedJob)
<二> startWorkEvent和cancelWorkEvent分别对应作用域生效状态和失效状态。当生命周期对应startWorkEvent时候,创建launchedJob保持Block(回调)函数作用域存活;当生命周期对应cancelWorkEvent时候,launchedJob执行取消动作,让Block作用域失效。
<三> suspendCancellableCoroutine挂起函数,直到resume执行才算整体函数执行完毕。当生命周期为onDestroy时候,执行resume回调,让整体suspendCancellableCoroutine函数执行完毕。
<四> finally语句块,当suspendCancellableCoroutine函数执行完毕后,launchedJob执行取消动作,调用removeObserver释放监听。
public suspend fun Lifecycle.repeatOnLifecycle(
state: Lifecycle.State,
block: suspend CoroutineScope.() -> Unit
) {
require(state !== Lifecycle.State.INITIALIZED) {
"repeatOnLifecycle cannot start work with the INITIALIZED lifecycle state."
}
if (currentState === Lifecycle.State.DESTROYED) {
return
}
// This scope is required to preserve context before we move to Dispatchers.Main
coroutineScope {
withContext(Dispatchers.Main.immediate) {
// Check the current state of the lifecycle as the previous check is not guaranteed
// to be done on the main thread.
if (currentState === Lifecycle.State.DESTROYED) return@withContext
// Instance of the running repeating coroutine
var launchedJob: Job? = null
// Registered observer
var observer: LifecycleEventObserver? = null
try {
// Suspend the coroutine until the lifecycle is destroyed or
// the coroutine is cancelled
suspendCancellableCoroutine<Unit> { cont ->
// Lifecycle observers that executes `block` when the lifecycle reaches certain state, and
// cancels when it falls below that state.
val startWorkEvent = Lifecycle.Event.upTo(state)
val cancelWorkEvent = Lifecycle.Event.downFrom(state)
val mutex = Mutex()
observer = LifecycleEventObserver { _, event ->
if (event == startWorkEvent) {
// Launch the repeating work preserving the calling context
launchedJob = this@coroutineScope.launch {
// Mutex makes invocations run serially,
// coroutineScope ensures all child coroutines finish
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)
}
}
}
}
}
repeatOnLifecycle总结
- repeatOnLifecycle只处理你当前关注的生命周期状态,当生命周期切换到非关注状态时候,会将作用域launchedJob给cancel掉,使其Block函数的作用域无法生效。
- 如果flow+collect 在repeatOnLifecycle作用域下执行,那么collect只会在当前Activity/Fragment对应的生命周期才可以收到新的数据回调。
补充:ShareFlow(reply为0) + collect + repeatOnLifecycle(Lifecycle.State.RESUMED) + 页面切换后,如果你可以预测到结果的话,那恭喜你这篇文章你学会大半了。
总结
- 有时间多看下kotlin源码,可以学习到别人架构层面的优秀设计,对我们以后设计某些架构有所帮助。