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() 就结束了。

这儿咱们再深化了解

  1. CompositionLocal 如何解析

  2. 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()

举个例子:

19.5 Compose CompositionContext 和 再谈 CompositionLocal

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 传递如下图

19.5 Compose CompositionContext 和 再谈 CompositionLocal