初始组合流程开始的第一步就是创建 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 中恢复挂起,这样又起到了统一控制作用。
Android 知识点归整
Android 性能调优系列:https://qr18.cn/FVlo89
Android 车载学习指南:https://qr18.cn/F05ZCM
Android Framework核心知识点笔记:https://qr18.cn/AQpN4J
Android 音视频学习笔记:https://qr18.cn/Ei3VPD
Jetpack全家桶(含Compose):https://qr18.cn/A0gajp
Kotlin 入门到精进:https://qr18.cn/CdjtAF
Flutter 基础到进阶实战:https://qr18.cn/DIvKma
Android 八大知识体系:https://qr18.cn/CyxarU
Android 中高级面试题锦:https://qr18.cn/CKV8OZ
后续如有新知识点,将会持续更新,尽请期待……