CompositionContext
总算可以说说 CompositionContext 了,放在最后是因为这东西没法上来直接说,需要咱们前面的累积。咱们来看下这个类的介绍
/**
* A [CompositionContext] is an opaque type that is used to logically "link" two compositions
* together. The [CompositionContext] instance represents a reference to the "parent" composition
* in a specific position of that composition's tree, and the instance can then be given to a new
* "child" composition. This reference ensures that invalidations and [CompositionLocal]s flow
* logically through the two compositions as if they were not separate.
*
* The "parent" of a root composition is a [Recomposer].
*
* @see rememberCompositionContext
*/
@OptIn(InternalComposeApi::class)
abstract class CompositionContext internal constructor()
总结一下
1. “子” CompositionContext 可以将两个 Composition 逻辑上链接在一起,发生逻辑上的父子关系。
CompositionContext 保存在 “父” Composition 的 SlotTable 中。
这个“子” CompositionContext 确保了两个 Composition 运用同一个 CompositionLocalMap 和 invalidations 逻辑。
2. 根 Composition 的 parentContext 就是 Activity#setContent() 时生成的 Recomposer 。
子 Composition 的 parentContext 运用 rememberCompositionContext() 办法生成。
Composition 逻辑父子关系中 CompositionContext 的类型用来判断 Composition 是不是最上层的“父” Composition。
internal class CompositionImpl(private val parent: CompositionContext){
val isRoot: Boolean = parent is Recomposer
}
CompositionContext 就两个实现类
CompositionContext
|-- Recomposer 根
|-- ComposerImpl.CompositionContextImpl 子
Recomposer 前面的章节现已介绍过了,这儿咱们来看 CompositionContextImpl
ComposerImpl.CompositionContextImpl
CompositionContextImpl 运用 rememberCompositionContext() 办法生成,这个办法在 Popup() 中有运用
@Composable
fun Popup() {
val parentComposition = rememberCompositionContext()
val popupLayout = remember {
PopupLayout().apply {
setContent(parentComposition) {
}
}
}
}
internal class PopupLayout(){
fun setContent(parent: CompositionContext, content: @Composable () -> Unit) {
setParentCompositionContext(parent)
this.content = content
shouldCreateCompositionOnAttachedToWindow = true
}
}
由源码可见 PopupLayout 的 parentContext 是 rememberCompositionContext() 办法生成的,而不是复用 ComposeView 在初始组合流程中生成的 Recomposer。
@Composable fun rememberCompositionContext(): CompositionContext {
return currentComposer.buildContext()
}
internal class ComposerImpl(
override fun buildContext(): CompositionContext {
startGroup(referenceKey, reference)
if (inserting)
writer.markGroup()
var holder = nextSlot() as? CompositionContextHolder
if (holder == null) {
//创建 CompositionContextHolder 和 CompositionContextImpl 目标
//holder.ref 是 CompositionContextImpl 目标
holder = CompositionContextHolder(
CompositionContextImpl(
compoundKeyHash,
forceRecomposeScopes
)
)
//holder 保存到“父” Composition 的 SlotTable 中
updateValue(holder)
}
//将父 Composition Slot中的 CompositionLocalMap 保存到
//CompositionContextImpl.compositionLocalScope 中
holder.ref.updateCompositionLocalScope(currentCompositionLocalScope())
endGroup()
//回来 CompositionContextImpl 目标
return holder.ref
}
}
生成一个被 CompositionContextHolder 持有的 CompositionContextImpl 目标 ,
将 holder 保存到“父” Composition 的 SlotTable 中
传递 CompositionLocalMap
回来 CompositionContextHolder 持有的 CompositionContextImpl 目标
随后这个 CompositionContextImpl 作为 PopupLayout 的 parentContext 参加到 PopupLayout 的初始组合和重组中。
CompositionContextHolder 结构简略,实现了 RememberObserver ,这是一个很重要的接口,咱们下章来学习它。
private class CompositionContextHolder(
val ref: ComposerImpl.CompositionContextImpl
) : RememberObserver {
override fun onRemembered() { }
override fun onAbandoned() {
ref.dispose()
}
override fun onForgotten() {
ref.dispose()
}
}
CompositionContext 类注释中说:
“子” CompositionContext 确保了两个 Composition 运用同一个 CompositionLocalMap 和 invalidations 逻辑。
同一个 CompositionLocalMap,buildContext() 源码有体现,具体后面会分析。
holder.ref.updateCompositionLocalScope(currentCompositionLocalScope())
同一个 invalidations 逻辑,看 CompositionContextImpl 源码就明白了。
//源码不完整
private inner class CompositionContextImpl(
override val compoundHashKey: Int,
override val collectingParameterInformation: Boolean
) : CompositionContext() {
override val effectCoroutineContext: CoroutineContext
get() = parentContext.effectCoroutineContext
override fun composeInitial(
composition: ControlledComposition,
content: @Composable () -> Unit
) {
parentContext.composeInitial(composition, content)
}
override fun invalidate(composition: ControlledComposition) {
//先处理父 composition 再处理自己
parentContext.invalidate(this@ComposerImpl.composition)
parentContext.invalidate(composition)
}
}
“子” Composition 中的 invalidations 逻辑 最后都是由“根” Composition 中的 Recomposer 来发起的。
再谈 CompositionLocal
14.1 的时分介绍过 CompositionLocal ,只说了 Android 默许增加的 CompositionLocal 和 如何自定义 CompositionLocal,说倒 CompositionLocalProviderI() 就结束了。
这儿咱们再深化了解
-
CompositionLocal 如何解析
-
CompositionLocal 如安在“父子” Composition 中传递
CompositionLocal解析流程
setContent 中 @Composable content 外层会增加默许的 CompositionLocalMap
original.setContent {
CompositionLocalProvider(LocalInspectionTables provides inspectionTable) {
ProvideAndroidCompositionLocals(owner, content)
}
}
@Composable
@OptIn(InternalComposeApi::class)
fun CompositionLocalProvider(vararg values: ProvidedValue<*>, content: @Composable () -> Unit) {
currentComposer.startProviders(values)
content()
currentComposer.endProviders()
}
CompositionLocalMap 的解析调用的不是 startGroup() ,而是 startProviders()
@InternalComposeApi
override fun startProviders(values: Array<out ProvidedValue<*>>) {
// 拿到当时 Composition 中的 CompositionLocalMap
val parentScope = currentCompositionLocalScope()
startGroup(providerKey, provider)
startGroup(providerValuesKey, providerValues)
// 将 values 解析成 CompositionLocalMap
val currentProviders = invokeComposableForResult(this) {
compositionLocalMapOf(values, parentScope)
}
endGroup()
val providers: CompositionLocalMap
val invalid: Boolean
if (inserting) {
//兼并 parentScope currentProviders
providers = updateProviderMapGroup(parentScope, currentProviders)
invalid = false
//标记当时插入操作的 wirter 现已有了 Provider
writerHasAProvider = true
} else {
/不是新增,判断 currentProviders 的和 SlotTable 中保存的是否相同
//相同就疏忽,不相同就更新
}
if (invalid && !inserting) {
providerUpdates[reader.currentGroup] = providers
}
providersInvalidStack.push(providersInvalid.asInt())
providersInvalid = invalid
//兼并后的 缓存在 providerCache
providerCache = providers
// groupKey groupObjectKey 不是LayoutNode 兼并后的 providers
// 将兼并后的 providers 保存到 当时 Composition 的 SlotTable 中
start(compositionLocalMapKey, compositionLocalMap, false, providers)
}
private fun currentCompositionLocalScope(group: Int? = null): CompositionLocalMap {
if (group == null)
//现已有缓存了直接回来
providerCache?.let { return it }
// 假如当时是插入操作且标记过 writerHasAProvider
// 代码的 if(inserting) 逻辑
if (inserting && writerHasAProvider) {
var current = writer.parent
while (current > 0) {
if (writer.groupKey(current) == compositionLocalMapKey &&
writer.groupObjectKey(current) == compositionLocalMap
) {
@Suppress("UNCHECKED_CAST")
val providers = writer.groupAux(current) as CompositionLocalMap
//缓存 providers 并回来
providerCache = providers
return providers
}
current = writer.parent(current)
}
}
//没有进行写操作,去 reader 里找 ,找到就缓存 providers 并回来
if (reader.size > 0) {
var current = group ?: reader.parent
while (current > 0) {
if (reader.groupKey(current) == compositionLocalMapKey &&
reader.groupObjectKey(current) == compositionLocalMap
) {
@Suppress("UNCHECKED_CAST")
val providers = providerUpdates[current]
?: reader.groupAux(current) as CompositionLocalMap
providerCache = providers
return providers
}
current = reader.parent(current)
}
}
//都没有就缓存 parentProvider 并回来
providerCache = parentProvider
return parentProvider
}
//默许 空
private var parentProvider: CompositionLocalMap = persistentHashMapOf()
举个例子:
CompositionLocal 传递过程
在 buildContext() 时把当时 Composition 中缓存的 ComposerImpl.providerCache 赋值给了CompositionContextImpl.compositionLocalScope。
// ComposerImpl.buildContext()
holder.ref.updateCompositionLocalScope(currentCompositionLocalScope())
//CompositionContextImpl
fun updateCompositionLocalScope(scope: CompositionLocalMap) {
compositionLocalScope = scope
}
CompositionContextImpl 作为 CompositionContext 作为参数创建 PopupLayout 的 Composition 和 Composer , PopupLayout 的初始组合敞开。
invokeComposable() 也就是解析 PopupLayout 的 @Composable content 之前会先调用 startRoot() 办法
private fun doCompose() {
trace("Compose:recompose") {
try {
startRoot() //CompositionLocal 在这儿传递
observeDerivedStateRecalculations() {
if (content != null) {
startGroup(invocationKey, invocation)
invokeComposable(this, content)
endGroup()
} else if () {
} else {}
}
endRoot()
} finally {}
}
}
此刻的 parentContext 就是 buildContext() 回来的 CompositionContextImpl 目标。
@OptIn(InternalComposeApi::class)
private fun startRoot() {
//CompositionContextImpl.compositionLocalScope
//将 ComposeView 中 Composer 缓存的 providers 赋值给
//当时 PopupLayout 中 Composer 的 parentProvider
parentProvider = parentContext.getCompositionLocalScope()
}
再履行 invokeComposable() 解析 PopupLayout 的 @Composable content 。
同样会先履行 startProviders(),此刻尽管 PopupLayout 刚刚敞开初始组合但第一次运转 currentCompositionLocalScope() 并不会回来空 Map ,而是传递过来的 providers。
//默许 空
//private var parentProvider: CompositionLocalMap = persistentHashMapOf()
// ↑
private var parentProvider = parentContext.getCompositionLocalScope()
private fun currentCompositionLocalScope(group: Int? = null): CompositionLocalMap {
providerCache = parentProvider
return parentProvider
}
此刻的 startProviders() 会以传递过来的 providers 为基础生成新的 providers 保存到 PopupLayout 的 Composition 的 SlotTable 中,同时缓存到 Composer 中。
在上面的例子中增加 Popup() , CompositionLocal 传递如下图