协程repeatOnLifecycle和launchWhenResumed区别

背景

  1. 在repeatOnLifecycle还没出现的时候,相信大家用协程过程中多多少少都接触过 Activity/Fragment 的launchWhenXXXX(launchWhenResumed),随着kotlin版本不断迭代,现在新版本出现一个repeatOnLifecycle来替代launchWhenXXXX的使用,接下来让我们逐步分析它们两者的区别。

两者函数实现区别

launchWhenResumed分析
  1. launchWhenXXXX方法最后都会走到Lifecycle.whenStateAtLeast方法里面,下面我们直接从Lifecycle.whenStateAtLeast分析。
public fun launchWhenResumed(block: suspend CoroutineScope.() -> Unit): Job = launch {
        lifecycle.whenResumed(block)
 }
whenStateAtLeast函数解析
  1. 从下面可以看出创建dispatcher(任务分发器,主要是存储任务和管理内部状态)和 job(父Job,关联当前block)和controller(任务管理器)。
  2. 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类解析
  1. init方法可以看出在非Destroy状态下,添加一个observer,用于观察Activity/Fragment的生命周期变化后进行回调。
  2. 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总结
  1. launchWhenResumed是在Activity/Fragment 在生命周期OnResume就会执行对应Suspend函数,并且执行后的函数作用域会一直存在,直到Activity/Fragment被销毁为止。
  2. 如果flow+collect 在launchWhenResumed作用域下执行,那么collect首次生效后就会一直持续下去,当flow生成新的数据时候,Activity/Fragment就算生命周期比Resume状态大的前提下,collect也会收到新的数据回调。

repeatOnLifecycle分析
  1. 从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总结
  1. repeatOnLifecycle只处理你当前关注的生命周期状态,当生命周期切换到非关注状态时候,会将作用域launchedJob给cancel掉,使其Block函数的作用域无法生效。
  2. 如果flow+collect 在repeatOnLifecycle作用域下执行,那么collect只会在当前Activity/Fragment对应的生命周期才可以收到新的数据回调。
    补充:ShareFlow(reply为0) + collect + repeatOnLifecycle(Lifecycle.State.RESUMED) + 页面切换后,如果你可以预测到结果的话,那恭喜你这篇文章你学会大半了。

总结

  1. 有时间多看下kotlin源码,可以学习到别人架构层面的优秀设计,对我们以后设计某些架构有所帮助。
©著作权归作者所有,转载或内容合作请联系作者
【社区内容提示】社区部分内容疑似由AI辅助生成,浏览时请结合常识与多方信息审慎甄别。
平台声明:文章内容(如有图片或视频亦包括在内)由作者上传并发布,文章内容仅代表作者本人观点,简书系信息发布平台,仅提供信息存储服务。

相关阅读更多精彩内容

友情链接更多精彩内容