19.6 Compose RememberObserver 原理

651 阅读6分钟

RememberObserver

实现该接口的对象可以监听它 在 Composition 中 的状态。

interface RememberObserver {
	//对象保存到组合时触发
  	//实现接口的对象被保存到 Composition.slotTable 中
    fun onRemembered()

	//对象从组合中移除时触发
  	//实现接口的对象从 Composition.slotTable 删除
    fun onForgotten()

	//对象保存到组合没有成功时触发
    fun onAbandoned()
}

使用 RememberObserver 接口需要注意两点

  1. 在对应的回调中编写逻辑
  2. 将对象写入到 Composition.slotTable 中 ( 接口就是用于监听对象在不在  Composition.slotTable  中的

除了上一章 buildContext() 内部创建的 CompositionContextHolder ,Effects Api 协程启动/取消也是使用 RememberObserver 接口实现的。

我们拿 LaunchedEffect 举例,在此之前先看一下 remember() 方法

remember() 方法

将参数 calculation 计算结果保存到当前 Composition.slotTable 中,所以 remember() 是实现 RememberObserver 接口对象写入 Composition.slotTable 的一种方式

碰到了,又不难 ,当然要重拳出击。

@Composable
inline fun <T> remember(crossinline calculation: @DisallowComposableCalls () -> T): T =
    currentComposer.cache(false, calculation)

@Composable
inline fun <T> remember(
    key1: Any?,
    crossinline calculation: @DisallowComposableCalls () -> T
): T {
    return currentComposer.cache(currentComposer.changed(key1), calculation)
}

remember 方法 key 参数用来产生判断当前保存的值是否失效的 invalid,calculation 参数用来产生需要保存的具体值,方法具体逻辑在 currentComposer.cache() 中。

remember 方法干了两件事 :①判断保存的值是否失效 ;②传递参数给 currentComposer.cache()

@ComposeCompilerApi
inline fun <T> Composer.cache(invalid: Boolean, block: @DisallowComposableCalls () -> T): T {
    @Suppress("UNCHECKED_CAST")
					
    return rememberedValue().let {
      	//如果保存的值失效 或者 第一次保存
        if (invalid || it === Composer.Empty) {
          	//执行 calculation 代码块 
            val value = block()
          	//将计算结构保存到 slotTable 中
            updateRememberedValue(value)
            value
        } else it
    } as T
}

internal class ComposerImpl(){
  	//如果是插入操作 返回 Composer.Empty,否则返回 slotTable 保存的值
    override fun rememberedValue(): Any? = nextSlot()
  	//updateValue() 细节稍后介绍
    override fun updateRememberedValue(value: Any?) = updateValue(value)
  
    @PublishedApi
    @OptIn(InternalComposeApi::class)
    internal fun nextSlot(): Any? = if (inserting) {
        validateNodeNotExpected()
        Composer.Empty
    } else reader.next().let { if (reusing) Composer.Empty else it }
}

只有在 invalid 为 true —— remember() 方法中通过 key 判断当前保存的值失效,和第一次保存的时候会执行 calculation 代码块并将结构保存/更新到 slotTable 中。

LaunchedEffect() 方法

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

方法实现出奇的简单

  1. 拿到 Recomposer 提供的 CoroutineContext (前面 19.2 分析过了)
  2. 使用 remember() 向 SlotTable 中插入一个 LaunchedEffectImpl 对象
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
    }
}

DisposableEffect() 、rememberCoroutineScope() 也是用这种  remember { RememberObserver } 方式实现的,

CompositionContextHolder 在 composerImpl.buildContext() 方法中直接调用 composerImpl.updateValue() 保存到了 Composition.slotTable 中。

LaunchedEffectImpl 实现 RememberObserver 接口,LaunchedEffectImpl 对象保存到 Composition.slotTable 时开启协程执行 block ,在 LaunchedEffectImpl 对象从 Composition.slotTable 中删除时取消协程。

RememberObserver 工作原理

RememberObserver 接口中的回调是如何触发的呢?

Composer 的特殊处理

ComposerImpl updateValue 方法在插入/更新 SlotTable 时会对实现了 RememberObserver 接口的对象进行特殊处理

    @PublishedApi
    @OptIn(InternalComposeApi::class)
    internal fun updateValue(value: Any?) {
        if (inserting) { //插入
            writer.update(value)//将 value 写入 slotTable
          	//如果 value 实现了 RememberObserver 接口
            if (value is RememberObserver) {
              	//record() 向 changes 列表添加一个 Change {}
              	//Change 执行调用 rememberManager.remembering(value)
                record { _, _, rememberManager -> rememberManager.remembering(value) }
              	//将 value 添加到 abandonSet
                abandonSet.add(value)
            }
        } else { //更新
          	
            val groupSlotIndex = reader.groupSlotIndex - 1
          	//将 value 添加到 abandonSet
            if (value is RememberObserver) {
                abandonSet.add(value)
            }
          	//recordSlotTableOperation 向 changes 列表添加一个 Change {}
            recordSlotTableOperation(forParent = true) { _, slots, rememberManager ->
              	//如果 value 实现了 RememberObserver 接口
              	//调用 rememberManager.remembering(value)
                if (value is RememberObserver) {
                    rememberManager.remembering(value)
                }
                // slots.set 将 value 值保存到 groupSlotIndex
				// 并返回 groupSlotIndex 之前保存的值 previous                                    
                when (val previous = slots.set(groupSlotIndex, value)) {
                  	//如果从 slotTable 删除的 previous 实现了 RememberObserver
                  	//调用 rememberManager.forgetting(previous)
                    is RememberObserver ->
                        rememberManager.forgetting(previous)
                  	//如果 previous 是 RecomposeScopeImpl 类型
                  	//previous.release() 
                    is RecomposeScopeImpl -> {
                        val composition = previous.composition
                        if (composition != null) {
                            previous.release()
                            composition.pendingInvalidScopes = true
                        }
                    }
                }
            }
        }
}

Composer 向 SlotTable 插入/更新实现了 RememberObserver 接口的 value 时都会做两个操作

  1. 将要插入 SlotTable 的 value  添加到  abandonSet
  2. 创建一个 Change 并将 这个 Change 添加到 changes 中,Change 执行时都会调用 rememberManager.remembering( value )

更新操作中如果被替换的对象 previous 也实现了 RememberObserver 接口,还会执行 rememberManager.forgetting( previous )

RememberObserver 回调这时候并没有触发。 只是使用了 abandonSet / changes / rememberManager 对 RememberObserver 处理。

Composition 我们逃课了 T_T 现在开始补课

abandonSet 、changes 和 ComposerImpl 一样是 CompositionImpl 中的属性,同时 abandonSet 、changes 也作为构造参数传递给 ComposerImpl ,也就是说 CompositionImpl 和 ComposerImpl 中使用的 abandonSet 和 changes 是相同的。

internal class CompositionImpl() : ControlledComposition {
    private val abandonSet = HashSet<RememberObserver>()    
	private val changes = mutableListOf<Change>()
  
    private val composer: ComposerImpl =
        ComposerImpl(
            applier = applier,
            parentContext = parent,
            slotTable = slotTable,
            abandonSet = abandonSet,
            changes = changes,
            lateChanges = lateChanges,
            composition = this
        ).also {
            parent.registerComposer(it)
    }  
}

abandonSet

abandonSet 所有添加到 SlotTable 中的 RememberObserver 都会被存入这个集合。每一个 RememberObserver 都可能被废弃。

在 CompositionImpl.applyChanges() 阶段 abandonSet 会作为构造参数生成 RememberEventDispatcher

changes

changes 在 Composition 中是跟 slotTable 同样重要的属性

如果说 slotTable 代表当前 UI 的状态,那么更新 UI 的操作就是对比当前的状态需要做哪些改变,每一个改变就是一个 Change 。

那么重组就可以理解成 解析需要重组的 @Composable 函数与 slotTable 中保存的状态做对比,需要变的地方记录成 一个 Change 保存到 changes 中,在 CompositionImpl.applyChanges() 阶段统一执行每一个 Change 来更新 UI 状态。

Change 是函数类型起的别名,重组流程还没有看透,这章我们只看如何操作 RememberObserver

internal typealias Change = (
    applier: Applier<*>, //操作 LayoutNode
    slots: SlotWriter, //操作 SlotTable
    rememberManager: RememberManager //操作 RememberObserver
) -> Unit

RememberManager

RememberManager 接口的实现类为 RememberEventDispatcher

    private class RememberEventDispatcher(
        private val abandoning: MutableSet<RememberObserver>
    ) : RememberManager {
        private val remembering = mutableListOf<RememberObserver>()
        private val forgetting = mutableListOf<RememberObserver>()
        private val sideEffects = mutableListOf<() -> Unit>()

        override fun remembering(instance: RememberObserver) {
            forgetting.lastIndexOf(instance).let { index ->
                if (index >= 0) {
                    forgetting.removeAt(index)
                    abandoning.remove(instance)
                } else {
                    remembering.add(instance)
                }
            }
        }

        override fun forgetting(instance: RememberObserver) {
            remembering.lastIndexOf(instance).let { index ->
                if (index >= 0) {
                    remembering.removeAt(index)
                    abandoning.remove(instance)
                } else {
                    forgetting.add(instance)
                }
            }
        }

        override fun sideEffect(effect: () -> Unit) {
            sideEffects += effect
        }

        fun dispatchRememberObservers() {
            // Send forgets
            if (forgetting.isNotEmpty()) {
                trace("Compose:onForgotten") {
                    for (i in forgetting.size - 1 downTo 0) {
                        val instance = forgetting[i]
                        if (instance !in abandoning) {
                            instance.onForgotten()
                        }
                    }
                }
            }

            // Send remembers
            if (remembering.isNotEmpty()) {
                trace("Compose:onRemembered") {
                    remembering.fastForEach { instance ->
                        abandoning.remove(instance)
                        instance.onRemembered()
                    }
                }
            }
        }

        fun dispatchSideEffects() {
            if (sideEffects.isNotEmpty()) {
                trace("Compose:sideeffects") {
                    sideEffects.fastForEach { sideEffect ->
                        sideEffect()
                    }
                    sideEffects.clear()
                }
            }
        }

        fun dispatchAbandons() {
            if (abandoning.isNotEmpty()) {
                trace("Compose:abandons") {
                    val iterator = abandoning.iterator()
                    while (iterator.hasNext()) {
                        val instance = iterator.next()
                        iterator.remove()
                        instance.onAbandoned()
                    }
                }
            }
        }
    }
}

它有两个作用:

1 管理以 abandoning 为基础的 RememberObserver

remembering()、forgetting() 执行时会以 abandoing 为基础向 remembering 、forgetting 集合中添加 RememberObserver。

需要执行 onRemembered() 的 RememberObserver 被添加到 remembering

需要执行 onForgotten() 的 RememberObserver 被添加到 forgetting

最后被留在 abandoning 中的 RememberObserver 就是需要执行 onAbandoned() 的 RememberObserver

dispatchRememberObservers() 会触发 remembering 中所有 RememberObserver.onRemembered() 和 forgetting 中所有 RememberObserver.onForgotten()

dispatchAbandons() 会触发 abandoning 中所有 RememberObserver.onAbandoned()

2 管理组合中的 SideEffect

遇到简单的重拳出击

SideEffect 每次都会被运行所以解析到 SideEffect 时直接将 SideEffect 记录成了一个 Change 添加到 changes 中

@Composable
@NonRestartableComposable
@OptIn(InternalComposeApi::class)
fun SideEffect(
    effect: () -> Unit
) {
    currentComposer.recordSideEffect(effect)
}
//--------------
    override fun recordSideEffect(effect: () -> Unit) {
      	//Change 执行时 将 effect 保存到 RememberEventDispatcher.sideEffects 中
        record { _, _, rememberManager -> rememberManager.sideEffect(effect) }
    }

dispatchSideEffects() 执行 sideEffects 中保存的所有 effect。

CompositionImpl.applyChanges()

事情还是要从初始组合说起 ,我们之前只分析到 composition.composeContent(content) 解析 @Composable 函数,现在我们知道函数中还有一部分代码在 ComposerImpl.updateValue() 时被解析成了 Change 保存到了 changes 中,所以初始组合最后会执行 composition.applyChanges()。

    internal override fun composeInitial(
        composition: ControlledComposition,
        content: @Composable () -> Unit
    ) {
        val composerWasComposing = composition.isComposing
        try {
            composing(composition, null) {
                composition.composeContent(content)
            }
        } catch (e: Exception) {
            processCompositionError(e, composition, recoverable = true)
            return
        }

		//...

        try {
            composition.applyChanges()
            composition.applyLateChanges()
        } catch (e: Exception) {
            processCompositionError(e)
            return
        }

    }

composition.applyChanges() 会调用 applyChangesInLocked() 来执行 changes 集合中所有的 Change 函数,我们只看 RememberObserver 相关的逻辑。

    private fun applyChangesInLocked(changes: MutableList<Change>) {
      //  RememberEventDispatcher 实现 RememberManager 接口
      val manager = RememberEventDispatcher(abandonSet)
        try {
            if (changes.isEmpty()) return
            trace("Compose:applyChanges") {
                applier.onBeginChanges()

                // Apply all changes
                slotTable.write { slots ->
                    val applier = applier
                    changes.fastForEach { change ->
                      	//执行 updateValue() 时添加的 Change 
                      //rememberManager.remembering(value)
                        change(applier, slots, manager)
                    }
                    changes.clear()
                }
                applier.onEndChanges()
            }
			//触发 onRemembered() onForgotten()
            manager.dispatchRememberObservers()
          	//执行 RememberEventDispatcher.sideEffects 中所有的 effect
            manager.dispatchSideEffects()

            if (pendingInvalidScopes) {
                trace("Compose:unobserve") {
                    pendingInvalidScopes = false
                    observations.removeValueIf { scope -> !scope.valid }
                    cleanUpDerivedStateObservations()
                }
            }
        } finally {
          	//如果所有的 Change 都被执行完了 
          	//且 RememberEventDispatcher.abandoning 不为空
          	//RememberEventDispatcher.abandoning 集合中所有的 RememberObserver
          	//执行 RememberObserver.onAbandoned()
            if (this.lateChanges.isEmpty())
                manager.dispatchAbandons()
        }
    }

applyChanges 阶段结束进入 slotTable 的 RememberOserver 触发 onRemembered() , 从 slotTable 中删除的 RememberOserver 触发 onForgotten(),仍然保存 abandonSet 在中的 RememberOserver 触发 onAbandoned()

applyChanges 阶段结束 changes 和 abandonSet 都被清空,初始组合/重组执行完毕 ^_^