19.2 Compose Recomposer启动流程分析
Posted datian1234
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了19.2 Compose Recomposer启动流程分析相关的知识,希望对你有一定的参考价值。
上一章初始组合流程开始的第一步就是创建 Recomposer 。
接着 Recomposer 又作为构造参数创建了 CompositionImpl 、 ComposerImpl ,又在 androidComposeView#onAttachedToWindow()
触发 onViewTreeOwnersAvailable 回后,调用 composeInitial() 开启初始组合。
Recomposer 重要程度不言而喻,本章我们从 Recomposer 启动流程来了解 Recomposer。
Recomposer 启动流程
class Recomposer(
effectCoroutineContext: CoroutineContext
) : CompositionContext()
官方的说法是 Recomposer 继承自 CompositionContext, 是一个调度器,用于执行重组来更新一个或多个 Composition 的可组合项中的变化。
归结起来 Recomposer 有两个主要的作用:
- 为 Compose 运行提供 CoroutineContext
- 启动初始组合和重组
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
- 启动流程中 while 循环所在协程的 CoroutineContext
- 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
- 在 View.createLifecycleAwareWindowRecomposer 中 lifecycle 监听中挂起/恢复协程
Lifecycle.Event.ON_START -> pausableClock?.resume()
Lifecycle.Event.ON_STOP -> pausableClock?.pause()
- 在 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官方认证微信卡片免费领取↓↓↓
以上是关于19.2 Compose Recomposer启动流程分析的主要内容,如果未能解决你的问题,请参考以下文章