19.2 Compose Recomposer启动流程分析

49 篇文章 1 订阅
6 篇文章 0 订阅

上一章初始组合流程开始的第一步就是创建 Recomposer 。

接着 Recomposer 又作为构造参数创建了 CompositionImpl 、 ComposerImpl ,又在 AndroidComposeView#onAttachedToWindow() 触发 onViewTreeOwnersAvailable 回后,调用 composeInitial() 开启初始组合。

Recomposer 重要程度不言而喻,本章我们从 Recomposer 启动流程来了解 Recomposer。

Recomposer 启动流程

class Recomposer(
    effectCoroutineContext: CoroutineContext
) : CompositionContext()

官方的说法是 Recomposer 继承自 CompositionContext, 是一个调度器,用于执行重组来更新一个或多个 Composition 的可组合项中的变化。

归结起来 Recomposer 有两个主要的作用:

  1. 为 Compose 运行提供 CoroutineContext
  2. 启动初始组合和重组

View.createLifecycleAwareWindowRecompose

启动流程要从 Recomposer 创建开始分析,View.createLifecycleAwareWindowRecomposer 先创建 Recompose 对象,在将其与 Lifecycle 关联 。

我们将这个方法拆开,先看上半部分 Recomposer 的创建。

fun View.createLifecycleAwareWindowRecomposer(
    coroutineContext: CoroutineContext = EmptyCoroutineContext,
    lifecycle: Lifecycle? = null
): Recomposer {
    // 1 AndroidUiDispatcher.Main
    val baseContext = if (coroutineContext[ContinuationInterceptor] == null ||
        coroutineContext[MonotonicFrameClock] == null
    ) {
        AndroidUiDispatcher.CurrentThread + coroutineContext
    } else coroutineContext
    // 2 包装 AndroidUiDispatcher.Main.frameClock
    val pausableClock = baseContext[MonotonicFrameClock]?.let {
        PausableMonotonicFrameClock(it).apply { pause() }
    }
    var systemDurationScaleSettingConsumer: MotionDurationScaleImpl? = null
    // 3 MotionDurationScaleImpl, scaleFactor = 1
    val motionDurationScale = baseContext[MotionDurationScale] ?: MotionDurationScaleImpl().also {
        systemDurationScaleSettingConsumer = it
    }
	//将 1 2 3 创建的 CoroutineContext 组合在一起
    val contextWithClockAndMotionScale =
        baseContext + (pausableClock ?: EmptyCoroutineContext) + motionDurationScale
    //创建 recomposer 对象                   
    val recomposer = Recomposer(contextWithClockAndMotionScale)
  	//创建 CoroutineScope 
    val runRecomposeScope = CoroutineScope(contextWithClockAndMotionScale)
    // 省略监听代码 
    return recomposer
}

从源码可以看出 effectCoroutineContext 是 1、2、3 处创建的三个 CoroutineContext 的组合。

AndroidUiDispatcher.Main 本身就是一个 CombinedContext ,包含了主线程的协程调度器 AndroidUiDispatcher 和基于 choreographer 的 AndroidUiFrameClock。

class AndroidUiDispatcher private constructor(
    val choreographer: Choreographer,
    private val handler: android.os.Handler
) : CoroutineDispatcher() {

	val frameClock: MonotonicFrameClock = AndroidUiFrameClock(choreographer)

	companion object {
        val Main: CoroutineContext by lazy {
            val dispatcher = AndroidUiDispatcher(
                if (isMainThread()) Choreographer.getInstance()
                else runBlocking(Dispatchers.Main) { Choreographer.getInstance() },
                HandlerCompat.createAsync(Looper.getMainLooper())
            )
            dispatcher + dispatcher.frameClock
        }
    }
}  

pausableClock 是使用 Latch 对 dispatcher.frameClock 进行封装,通过 latch.closeLatch() / latch.openLatch() 来控制 frameClock.withFrameNanos(onFrame) ,最后组合时会替换掉相同 key 的 dispatcher.frameClock。

motionDurationScale 提供时长放大的系数,默认是 1 。拿动画来举例,如果动画时长是 100 ms ,此时 motionDurationScale 的 scaleFactor 是 10 的话, 动画的真正耗时就会变成 1000 ms 。

使用这个组合的 CoroutineContext 创建了 Recomposer 对象和一个 CoroutineScope 之后就是方法的后半部分,添加了两个监听。

有一点需要注意:这个 CoroutineContext 中目前为止是没有 Job 的

    addOnAttachStateChangeListener(
        object : View.OnAttachStateChangeListener {
            override fun onViewAttachedToWindow(v: View) {}
            override fun onViewDetachedFromWindow(v: View) {
                removeOnAttachStateChangeListener(this)
                recomposer.cancel()
            }
        }
    )
    viewTreeLifecycle.addObserver(
        object : LifecycleEventObserver {
            override fun onStateChanged(
                lifecycleOwner: LifecycleOwner,
                event: Lifecycle.Event
            ) {
                val self = this
                when (event) {
                    Lifecycle.Event.ON_CREATE -> {
                        runRecomposeScope.launch(start = CoroutineStart.UNDISPATCHED) {
                            var durationScaleJob: Job? = null
                            try {
                                durationScaleJob = systemDurationScaleSettingConsumer?.let {
                                    val durationScaleStateFlow = getAnimationScaleFlowFor(
                                        context.applicationContext
                                    )
                                    it.scaleFactor = durationScaleStateFlow.value
                                    launch {
                                        durationScaleStateFlow.collect { scaleFactor ->
                                            it.scaleFactor = scaleFactor
                                        }
                                    }
                                }
                                recomposer.runRecomposeAndApplyChanges()
                            } finally {
                                durationScaleJob?.cancel()
                                lifecycleOwner.lifecycle.removeObserver(self)
                            }
                        }
                    }
                    Lifecycle.Event.ON_START -> pausableClock?.resume()
                    Lifecycle.Event.ON_STOP -> pausableClock?.pause()
                    Lifecycle.Event.ON_DESTROY -> {
                        recomposer.cancel()
                    }
                }
            }
        }
    )

ON_START,ON_STOP 中使用 pausableClock 恢复和挂起 frameClock.withFrameNanos(onFrame)

ON_CREATE 时在 runRecomposeScope 中开启协程调用 recomposer.runRecomposeAndApplyChanges()。

runRecomposeScope.launch{
  	//监听 scaleFactor 变化及时赋值给 CoroutineContext 中的 motionDurationScale  
	durationScaleJob = systemDurationScaleSettingConsumer?.let {
		val durationScaleStateFlow = getAnimationScaleFlowFor(
			context.applicationContext
			)
		it.scaleFactor = durationScaleStateFlow.value
		launch {
			durationScaleStateFlow.collect { scaleFactor ->
				it.scaleFactor = scaleFactor
			}
		}
	}
	recomposer.runRecomposeAndApplyChanges()
}

recomposer.runRecomposeAndApplyChanges

方法的作用是在协程中开启一个和 Recomposer 生命周期相同的循环,循环体中先判断当前是否有要处理的工作,如果没有就挂起协程,如果有就在 frameClock.withFrameNanos(onFrame) 中开启重组逻辑。

方法是在 ON_CREATE 时启动的,在生命周期中这个方法只会调用一次。

suspend fun runRecomposeAndApplyChanges() = recompositionRunner { block }

private suspend fun recompositionRunner(block: suspend CoroutineScope.(parentFrameClock: MonotonicFrameClock) -> Unit

runRecomposeAndApplyChanges 具体实现是调用 recompositionRunner 并传递了 block ,recompositionRunner 作用是设置重组运行的环境,具体业务逻辑在 block 中实现。

    @OptIn(ExperimentalComposeApi::class)
    private suspend fun recompositionRunner(
        block: suspend CoroutineScope.(parentFrameClock: MonotonicFrameClock) -> Unit
    ) {
      	//pausableClock 
        val parentFrameClock = coroutineContext.monotonicFrameClock
        withContext(broadcastFrameClock) {
			//runnerJob
            val callingJob = coroutineContext.job
            registerRunnerJob(callingJob)

			//为快照添加监听
            val unregisterApplyObserver = Snapshot.registerApplyObserver { changed, _ ->	
              	//如果快照改变后 Recompose 是 PendingWork 状态
              	//恢复 workContinuation 协程
                synchronized(stateLock) {
                    if (_state.value >= State.Idle) {
                       snapshotInvalidations += changed
                        deriveStateLocked()
                    } else null
                }?.resume(Unit)
            }

            addRunning(recomposerInfo)

            try {
				//初始化时将所有 Composition 设置成 invalidate
                synchronized(stateLock) {
                    knownCompositions.fastForEach { it.invalidateAll() }
                }
				//以 pausableClock 为参数调用 block
                coroutineScope {
                    block(parentFrameClock)
                }
            } finally {
                unregisterApplyObserver.dispose()
                synchronized(stateLock) {
                    if (runnerJob === callingJob) {
                        runnerJob = null
                    }
                    deriveStateLocked()
                }
                removeRunning(recomposerInfo)
            }
        }
    }

方法中修改了当前 CoroutineContext[MonotonicFrameClock] 由 pausableClock 替换成 broadcastFrameClock , 原来的pausableClock 以参数的形式传递给了 block 。为快照添加全局监听,改变时调用 deriveStateLocked() 。

接着看 block 中的实现,删除了部分代码

    suspend fun runRecomposeAndApplyChanges() = recompositionRunner { parentFrameClock ->

		//省略 
        while (shouldKeepRecomposing) {
          	//判断是否需要挂起
            awaitWorkAvailable(
            if (
                synchronized(stateLock) {
                    if (!hasFrameWorkLocked) {
                        recordComposerModificationsLocked()
                        !hasFrameWorkLocked
                    } else false
                }
            ) continue
            parentFrameClock.withFrameNanos { frameTime ->
				//确保 broadcastFrameClock awaiter 中会发生的改变
              	//在同一帧处理
                if (broadcastFrameClock.hasAwaiters) {
                    trace("Recomposer:animation") {
                        broadcastFrameClock.sendFrame(frameTime)
                        Snapshot.sendApplyNotifications()
                    }
                }

                trace("Recomposer:recompose") {
                  	//省略 

                  	//启动重组
                    while (toRecompose.isNotEmpty() || toInsert.isNotEmpty()) {
                        try {
                            toRecompose.fastForEach { composition ->
                                alreadyComposed.add(composition)
                                performRecompose(composition, modifiedValues)?.let {
                                    toApply += it
                                }

                        } catch (e: Exception) {
                            processCompositionError(e, recoverable = true)
                            clearRecompositionState()
                            return@withFrameNanos
                        } finally {
                            toRecompose.clear()
                        }
                      	//省略 

                    }
					//省略 
                    synchronized(stateLock) {
                        deriveStateLocked()
                    }
                }
            }

            discardUnusedValues()
        }
    }

通过循环条件 shouldKeepRecomposing 可以看出 while 循环会一直运行直到 Recomposer 关闭。

    private val shouldKeepRecomposing: Boolean
        get() = synchronized(stateLock) { !isClosed } ||
            effectJob.children.any { it.isActive } 

    fun close() {
        if (effectJob.complete()) {
            synchronized(stateLock) {
                isClosed = tru
            }
        }
    }

方法体中先判断是否需要挂起,如果不需要最后会调用 performRecompose() 来执行重组,这个循环会在 Recomposer 声明周期中一直运行。

Recomposer 启动流程有两个重要的地方

  • while 循环的挂起与恢复
  • CoroutineContext 的变化

挂起与恢复

循环的第一步是调用 awaitWorkAvailable() 方法,检查当前是否有需要执行的工作,如果没有就挂起并将其赋值给 workContinuation 属性。

    private val hasSchedulingWork: Boolean
        get() = synchronized(stateLock) {
            snapshotInvalidations.isNotEmpty() ||
                compositionInvalidations.isNotEmpty() ||
                broadcastFrameClock.hasAwaiters
        }

    private suspend fun awaitWorkAvailable() {
        if (!hasSchedulingWork) {
            suspendCancellableCoroutine<Unit> { co -
                synchronized(stateLock) {
                    if (hasSchedulingWork) {
                        co.resume(Unit
                    } else {
                        workContinuation = co
                    }
                }
            }
        }
}

恢复操作与 deriveStateLocked() 有关

    private fun deriveStateLocked(): CancellableContinuation<Unit>? {
        if (_state.value <= State.ShuttingDown) {
            //..
        }

        val newState = when {
            errorState != null -> {
                State.Inactive
           }
            runnerJob == null -> {
            //..
            else -> State.Idle
        }

        _state.value = newState
        return if (newState == State.PendingWork) {
            workContinuation.also {
                workContinuation = null
            }
        } else null
    }

当新状态是 State.PendingWork 时 deriveStateLocked() 会返回挂起的协程 workContinuation 。

在 recompositionRunner() 注册快照全局监听的回调方法中就是具体恢复协程的实现,类似的调用 Recomposer 中有多处。

                synchronized(stateLock) {
                    if (_state.value >= State.Idle) {
                        snapshotInvalidations += change
                        deriveStateLocked()
                    } else nul
                }?.resume(Unit)

Recomposer 中的 CoroutineContext

Recomposer 中的 CoroutineContext 有两处 CoroutineContext

  1. 启动流程中 while 循环所在协程的 CoroutineContext
  2. Recomposer 对外(系统其他组件不是开发者)提供的 effectCoroutineContext

它们最先都来自 View.createLifecycleAwareWindowRecomposer ,作为参数创建了 Recomposer 和 runRecomposeScope。

其中没有 Job

此时启动流程还没有开始,所以 runRecomposeScope 中 CoroutineContext 并没有变化。 Recomposer 已经创建好了,先来看 Recomposer.effectCoroutineContext

Recomposer.effectCoroutineContext

// 等号右边的 effectCoroutineContext 是构造参数
// broadcastFrameClock 的 key 是 MonotonicFrameClock
internal override val effectCoroutineContext: CoroutineContext 
    effectCoroutineContext + broadcastFrameClock + effectJob

effectCoroutineContext 属性随着 Recomposer 对象一起创建,看源码可知 effectCoroutineContext 将传入参数中的 pausableClock 替换成了 broadcastFrameClock ,并添加了 effectJob。

effectJob

用于控制所有 Effects Api 或者在 @Composable 函数中启动协程的父Job

fun LaunchedEffect(
    key1: Any?,
    block: suspend CoroutineScope.() -> Unit
) {
    val applyContext = currentComposer.applyCoroutineContext
    remember(key1) { LaunchedEffectImpl(applyContext, block) }
}

internal class LaunchedEffectImpl(
    parentCoroutineContext: CoroutineContext,
    private val task: suspend CoroutineScope.() -> Unit
) : RememberObserver {
    private val scope = CoroutineScope(parentCoroutineContext)
    private var job: Job? = null

    override fun onRemembered() {
        job?.cancel("Old job was still running!")
        job = scope.launch(block = task)
    }

    override fun onForgotten() {
        job?.cancel()
        job = null
    }

    override fun onAbandoned() {
        job?.cancel()
        job = null
    }
}
//Context 中的 Job 作为父Job 创建 CoroutineScope 中的 Job
public fun CoroutineScope(context: CoroutineContext): CoroutineScope =
    ContextScope(if (context[Job] != null) context else context + Job())
@Composable
inline fun rememberCoroutineScope(
    crossinline getContext: @DisallowComposableCalls () -> CoroutineContext =
        { EmptyCoroutineContext }
): CoroutineScope {
    val composer = currentComposer
    val wrapper = remember {
        CompositionScopedCoroutineScopeCanceller(
            createCompositionCoroutineScope(getContext(), composer)
        )
    }
    return wrapper.coroutineScope
}

@PublishedApi
@OptIn(InternalComposeApi::class)
internal fun createCompositionCoroutineScope(
    coroutineContext: CoroutineContext,
    composer: Composer
) = if (coroutineContext[Job] != null) {
    CoroutineScope(
        Job().apply {
            completeExceptionally(
                IllegalArgumentException(
                    "CoroutineContext supplied to " +
                        "rememberCoroutineScope may not include a parent job"
                )
            )
        }
    )
} else {
    val applyContext = composer.applyCoroutineContext
  	//Context 中的 Job 作为父Job 创建 CoroutineScope 中的 Job
    CoroutineScope(applyContext + Job(applyContext[Job]) + coroutineContext)
}
//上章分析时我们知道 parentContext 就是 Recomposer 对象,    
override val applyCoroutineContext: CoroutineContext
        @TestOnly get() = parentContext.effectCoroutineContext

所以 rememberCoroutineScope() 和 Effects Api 创建的协程都是 Recomposer.effectCoroutineContext 中 effectJob 的子 Job。

启动 Recomposer 协程中的 CoroutineContext

View.createLifecycleAwareWindowRecomposer 后半段 Lifecycle 监听的 ON_CREATE 中 runRecomposeScope.launch{} 会默认创建一个 StandaloneCoroutine 类型的 Job,这个 Job 会作为 Recomposer 执行流程中所有协程的父 Job ,控制 Recomposer 运行协程。随后执行 recompositionRunner()

    @OptIn(ExperimentalComposeApi::class)
    private suspend fun recompositionRunner(
        block: suspend CoroutineScope.(parentFrameClock: MonotonicFrameClock) -> Unit
    ) {
      	//pausableClock 
        val parentFrameClock = coroutineContext.monotonicFrameClock
        withContext(broadcastFrameClock) {
			//runnerJob
            val callingJob = coroutineContext.job
            registerRunnerJob(callingJob)
            try {
                coroutineScope {
                    block(parentFrameClock)
                }
            } finally {}
        }
    }

先使用 withContext() 方法 将pausableClock 替换成了 broadcastFrameClock,跟 Recomposer.effectCoroutineContext 中的是一个对象。接着调用 registerRunnerJob() 方法将 runRecomposeScope.launch{} 中创建的 Job 赋值给 Recompose.runnerJob。

至此 Compose 运行环境中的两个父 Job 都出现了: Recompose.runnerJob、effectJob。

broadcastFrameClock

Recomposer 两个 CoroutineContext 中的 MonotonicFrameClock 都是它 。

Recomposer.effectCoroutineContext 是在赋值时替换后直接对外提供的, runRecomposeScope.coroutineContext 却不是简单的替换,先了解 BroadcastFrameClock 作用再来详细分析。

class BroadcastFrameClock(
    private val onNewAwaiters: (() -> Unit)? = null
) : MonotonicFrameClock {

    private class FrameAwaiter<R>(val onFrame: (Long) -> R, val continuation: Continuation<R>) {
        fun resume(timeNanos: Long) {
            continuation.resumeWith(runCatching { onFrame(timeNanos) })
        }
    }

    private val lock = Any()
    private var failureCause: Throwable? = null
    private var awaiters = mutableListOf<FrameAwaiter<*>>()
    private var spareList = mutableListOf<FrameAwaiter<*>>()

    val hasAwaiters: Boolean get() = synchronized(lock) { awaiters.isNotEmpty() }

    fun sendFrame(timeNanos: Long) {
        synchronized(lock) {
            val toResume = awaiters
            awaiters = spareList
            spareList = toResume

            for (i in 0 until toResume.size) {
                toResume[i].resume(timeNanos)
            }
            toResume.clear()
        }
    }

    override suspend fun <R> withFrameNanos(
        onFrame: (Long) -> R
    ): R = suspendCancellableCoroutine { co ->
        lateinit var awaiter: FrameAwaiter<R>
        val hasNewAwaiters = synchronized(lock) {
            val cause = failureCause
            if (cause != null) {
                co.resumeWithException(cause)
                return@suspendCancellableCoroutine
            }
            awaiter = FrameAwaiter(onFrame, co)
            val hadAwaiters = awaiters.isNotEmpty()
            awaiters.add(awaiter)
            !hadAwaiters
        }

        co.invokeOnCancellation {
            synchronized(lock) {
                awaiters.remove(awaiter)
            }
        }

        if (hasNewAwaiters && onNewAwaiters != null) {
            try {
                onNewAwaiters.invoke()
            } catch (t: Throwable) {
                fail(t)
            }
        }
    }

    private fun fail(cause: Throwable) {
        synchronized(lock) {
            if (failureCause != null) return
            failureCause = cause
            awaiters.fastForEach { awaiter ->
                awaiter.continuation.resumeWithException(cause)
            }
            awaiters.clear()
        }
    }

    fun cancel(
        cancellationException: CancellationException = CancellationException("clock cancelled")
    ) {
        fail(cancellationException)
    }
}

不同之处在于 BroadcastFrameClock.withFrameNanos() 并不会直接运行 onFrame() 回调,而是把 onFrame() 封装成了 FrameAwaiter 保存到 awaiters 中在 sendFrame() 方法中一起执行。

分析 runRecomposeScope.coroutineContext 中的 MonotonicFrameClock

  1. 在 View.createLifecycleAwareWindowRecomposer 中 lifecycle 监听中挂起/恢复协程
Lifecycle.Event.ON_START -> pausableClock?.resume()
Lifecycle.Event.ON_STOP -> pausableClock?.pause()
  1. 在 recompositionRunner() 中替换成与 Recomposer.effectCoroutineContex 中相同的 broadcastFrameClock,将原来的 pausableClock 作为参数传递给 block

3.在 block while 循环中设置 pausableClock.onFrame() 为触发时先调用 broadcastFrameClock.sendFrame() 执行 broadcastFrameClock.awaiters 中的 onFrame()

parentFrameClock.withFrameNanos { frameTime ->

                if (broadcastFrameClock.hasAwaiters) {
                    trace("Recomposer:animation") {
                        broadcastFrameClock.sendFrame(frameTime)

                        Snapshot.sendApplyNotifications()
                    }
                }
}

这样 @Composable 函数中所有使用 CoroutineContext[MonotonicFrameClock] 监听的 onFrame() 会添加到 broadcastFrameClock.awaiters 中等待 pausableClock.onFrame() 一起执行,提高工作效率。

此外 pausableClock 会在 ON_START ,ON_STOP 中恢复挂起,这样又起到了统一控制作用。

作者:给大佬们点赞
链接:https://juejin.cn/post/7199908667642347579

最后

如果想要成为架构师或想突破20~30K薪资范畴,那就不要局限在编码,业务,要会选型、扩展,提升编程思维。此外,良好的职业规划也很重要,学习的习惯很重要,但是最重要的还是要能持之以恒,任何不能坚持落实的计划都是空谈。

如果你没有方向,这里给大家分享一套由阿里高级架构师编写的《Android八大模块进阶笔记》,帮大家将杂乱、零散、碎片化的知识进行体系化的整理,让大家系统而高效地掌握Android开发的各个知识点。

在这里插入图片描述
相对于我们平时看的碎片化内容,这份笔记的知识点更系统化,更容易理解和记忆,是严格按照知识体系编排的。

全套视频资料:

一、面试合集
在这里插入图片描述
二、源码解析合集

在这里插入图片描述
三、开源框架合集

在这里插入图片描述
欢迎大家一键三连支持,若需要文中资料,直接点击文末CSDN官方认证微信卡片免费领取↓↓↓

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值