关于协程,你知道LifecycleScope吗,超详细解释给你听!

whenResumed {

// do

}

}

// or

lifecycleScope.launchWhenResumed {

// do

}

whenResumedlaunchWhenResumed执行时机一样,区别在于:

  • whenResumed 可以有返回结果

  • launchWhenResumed 返回的是Job对象

共有三个对应生命周期的扩展函数:

  • whenCreated

  • whenStarted

  • whenResumed

使用非常简单,关键在于它是怎么保证不会内存泄露的,又是怎么知道在某个生命周期的时候去执行协程的?

源码分析


1、如何保证不会内存泄漏的

先看lifecycleScope源码:

val LifecycleOwner.lifecycleScope: LifecycleCoroutineScope

get() = lifecycle.coroutineScope

继承自LifecycleCoroutineScope,而LifecycleCoroutineScope是CoroutineScope的子类(协程层级关系)。

get()返回lifecycle.coroutineScope

这里有一个源码小技巧,当继承对象与返回对象不一致时,那么返回对象多半为继承对象的子类。

继续看lifecycle.coroutineScope:

public val Lifecycle.coroutineScope: LifecycleCoroutineScope

get() {

while (true) {

val existing = mInternalScopeRef.get() as LifecycleCoroutineScopeImpl?

if (existing != null) {

return existing

}

val newScope = LifecycleCoroutineScopeImpl(

this,

SupervisorJob() + Dispatchers.Main.immediate

)

if (mInternalScopeRef.compareAndSet(null, newScope)) {

newScope.register()

return newScope

}

}

}

果不其然,也是继承LifecycleCoroutineScope。

关键在于,通过LifecycleCoroutineScopeImpl创建了协程,默认主线程,随后又调用了newScope.register()

继续看LifecycleCoroutineScopeImpl:

internal class LifecycleCoroutineScopeImpl(

override val lifecycle: Lifecycle,

override val coroutineContext: CoroutineContext

) : LifecycleCoroutineScope(), LifecycleEventObserver {

//…

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()

}

}

}

register()方法中添加了LifecycleEventObserver接口的监听,LifecycleEventObserver会在onStateChanged方法中派发当前生命周期,关键来了,在onStateChanged回调中,判断当前生命周期是destroyed的时候,移除监听,并取消协程

至此,相信大部分同学都明白了为什么不会造成内存泄露了,因为在页面destroyed的时候,协程会取消,并不会继续执行,而MainScope是需要手动取消的,否则会有内存泄露的风险。

插曲,我们进一步思考,在其他的开发场景中,也可以学习源码通过添加LifecycleEventObserver监听的方式,做回收清理操作,来避免内存泄漏。

author:yechaoa

2、如何知道在某个生命周期去执行协程

lifecycleScope.launchWhenResumed为例,一探究竟。

fun launchWhenResumed(block: suspend CoroutineScope.() -> Unit): Job = launch {

lifecycle.whenResumed(block)

}

调用whenResumed

suspend fun Lifecycle.whenResumed(block: suspend CoroutineScope.() -> T): T {

return whenStateAtLeast(Lifecycle.State.RESUMED, block)

}

接着调用whenStateAtLeast,并传入一个具体生命周期状态作为标识

继续看whenStateAtLeast:

suspend fun Lifecycle.whenStateAtLeast(

minState: Lifecycle.State,

block: suspend CoroutineScope.() -> 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,并向下传入接收的具体状态,同时还有一个调度队列dispatcher.dispatchQueue。

接着看LifecycleController:

@MainThread

internal class LifecycleController(

private val lifecycle: Lifecycle,

private val minState: Lifecycle.State,

private val dispatchQueue: DispatchQueue,

parentJob: Job

) {

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)

}

}

//…

}

init初始化的时候,添加LifecycleEventObserver监听(又是一个使用案例,不过这里用的是lambda写法)。

在回调中,对生命周期进行了判断,当大于当前状态的时候,也就是生命周期执行到当前状态的时候,会调用dispatchQueue.resume()执行队列,也就是协程开始执行

dispatchQueue.resume:

@MainThread

fun resume() {

if (!paused) {

return

}

check(!finished) {

“Cannot resume a finished dispatcher”

}

paused = false

drainQueue()

}

//…

@MainThread

fun drainQueue() {

if (isDraining) {

// Block re-entrant calls to avoid deep stacks

return

}

try {

isDraining = true

while (queue.isNotEmpty()) {

if (!canRun()) {

break

}

queue.poll()?.run()

}

} finally {

isDraining = false

}

}

关于怎么获取到当前生命周期状态的,就涉及到Lifecycle相关的知识了,简而言之,不管是Activity还是Fragment,都是LifecycleOwner,其实是父类实现的,比如ComponentActivity。

在父类中通过ReportFragmentActivityLifecycleCallbacks接口来派发当前生命周期状态,具体使用哪种派发方式要看Api等级是否在29(10.0)及以上,及 则后者。

验证分析

最后

由于题目很多整理答案的工作量太大,所以仅限于提供知识点,详细的很多问题和参考答案我都整理成了 PDF文件


《Android学习笔记总结+移动架构视频+大厂面试真题+项目实战源码》点击传送门,即可获取!
LifecycleOwner`,其实是父类实现的,比如ComponentActivity。

在父类中通过ReportFragmentActivityLifecycleCallbacks接口来派发当前生命周期状态,具体使用哪种派发方式要看Api等级是否在29(10.0)及以上,及 则后者。

验证分析

最后

由于题目很多整理答案的工作量太大,所以仅限于提供知识点,详细的很多问题和参考答案我都整理成了 PDF文件

[外链图片转存中…(img-5PHcl2us-1715191493411)]

[外链图片转存中…(img-wOmhJx20-1715191493412)]

[外链图片转存中…(img-2yaLVDCR-1715191493413)]
《Android学习笔记总结+移动架构视频+大厂面试真题+项目实战源码》点击传送门,即可获取!

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值