上一章剖析了 Recomposer , resolveParentCompositionContext() 创立了 Recomposer 后作为参数调用了 setContent() 办法创立了 Composition 目标保存到了 ComposeView 中。

internal fun AbstractComposeView.setContent(
  parent: CompositionContext,
  content: @Composable () -> Unit
): Composition {
  GlobalSnapshotManager.ensureStarted()
 	//生成 AndroidComposeView 目标增加到 ComposeView 中
  val composeView =
    if (childCount > 0) {
      getChildAt(0) as? AndroidComposeView
    } else {
      removeAllViews(); null
    } ?: AndroidComposeView(context).also { addView(it.view, DefaultLayoutParams) }
  return doSetContent(composeView, parent, content)
}
//生成 WrappedComposition 目标赋值给 ComposeView.composition
private fun doSetContent(
  owner: AndroidComposeView,
  parent: CompositionContext,
  content: @Composable () -> Unit
): Composition {
  if (inspectionWanted(owner)) {
    owner.setTag(
      R.id.inspection_slot_table_set,
      Collections.newSetFromMap(WeakHashMap<CompositionData, Boolean>())
    )
    enableDebugInspectorInfo()
  }
 	//owner.root => 根 LayoutNode, [AndroidComposeView.root]
  val original = Composition(UiApplier(owner.root), parent)
  val wrapped = owner.view.getTag(R.id.wrapped_composition_tag)
    as? WrappedComposition
    ?: WrappedComposition(owner, original).also {
      owner.view.setTag(R.id.wrapped_composition_tag, it)
    }
  wrapped.setContent(content)
  return wrapped
}

暂时先不剖析 wrapped.setContent() ,办法履行结束后 ComposeView 中:

  1. 多了一个 AndroidComposeView 类型的子 View

  2. ComposeView.composition 特点被赋值成 WrappedComposition 目标

AndroidComposeView 是真正担任显现 UI 的组件。

Composition 担任保存 @Composable content 函数解析后的一切信息 (位置、状态、运转环境、函数体本身等) 。

Composition 中保存的数据与 AndroidComposeView 中显现的 UI 是对应联系。能够理解成他们是 ComposeView中的 State – UI 。

19.3 ComposeView 中的 Composition

AndroidComposeView 现已不陌生了,前面剖析测绘和事件传递的章节都介绍过。@Composable 函数解析后 UI 相关的部分会解析成 LayoutNode 增加到 AndroidComposeView.root 中。

LayoutNode 它本身具有丈量、制作功用。增加到 AndroidComposeView.root 后会跟随 View 的测绘回调完成 UI 的显现。Compose 只需要在初始组合或重组中增加或更新 LayoutNode ,在 View 对应的回调中 LayoutNode 我测我自己,我画我自己 。

Composition

AndroidComposeView 中 UI 保存在 root 跟节点,Composition 中代表状态的信息保存在 slotTable 特点中

SlotTable

SlotTable 引荐 探究 Jetpack Compose 内核:深化 SlotTable 体系

  • @Composable 函数解析时以 group 为单位
  • SlotTable 中 slots : Array<Any?> 保存 @Composable 函数解析后的具体信息
  • SlotTable 中 groups : IntArray 保存每个 group 的信息,配合 slots 完成树形结构
  • SlotTable 中数据根据 Gap Buffer 完成
  • SlotReader 、 SlotWriter 担任对 SlotTable 读写
  • 同一时间能够有多个 reader 进行读操作,但是同一时间只能有一个 writer 进行写操作,且在对 SlotTable 进行写操作时不能够有读操作
  • SlotReader 、 SlotWriter 配合快照一同运用 一文看懂 Jetpack Compose 快照体系

Composer

Composition 中还有一个重要特点 composer 。上一章说 Recomposer 是建议初始组合和重组,具体的工作是交给 Composer 来履行的。

19.3 ComposeView 中的 Composition

19.1 中只剖析到 doCompose() , 接着看初始组合中 doCompose() 到底做了些什么。

  private fun doCompose(
    invalidationsRequested: IdentityArrayMap<RecomposeScopeImpl, IdentityArraySet<Any>?>,
    content: (@Composable () -> Unit)?
  ){
          if (content != null) {
            startGroup(invocationKey, invocation)
            invokeComposable(this, content)
            endGroup()
          }
    }

咱们疏忽 group 信息保护 ,再次引荐 探究 Jetpack Compose 内核:深化 SlotTable 体系。

doCompose() 办法调用 invokeComposable(this, content)

19.3 ComposeView 中的 Composition

最后会履行 ComposableLambda.jvm.kt 中的 invoke()

override operator fun invoke(c: Composer, changed: Int): Any? {
  	//创立 RecomposeScope 保存到 SlotTable中
        val c = c.startRestartGroup(key)
        trackRead(c)
        val dirty = changed or if (c.changed(this)) differentBits(0) else sameBits(0)
	//履行当时 Compose 函数
        val result = (_block as (c: Composer, changed: Int) -> Any?)(c, dirty)
        //将当时的 Compose 函数保存到 RecomposeScope.block 中
        c.endRestartGroup()?.updateScope(this as (Composer, Int) -> Unit)
        return result
    }

@Composable 注解的函数经过 Compose Compiler 编译后会发生如下改变

@Composable fun test(){
	//...
}
fun test($composer: Composer<*>){
    $composer.start(123)
  	//...
    $composer.end()
}
  • 函数中增加 $composer 参数,这个参会跟着函数调用依次传递到最下级 @Composable 注解的函数
  • 每个函数调用时都会在 SlotTable 中增加 group 信息

RecomposeScope

startRestartGroup 除了在 SlotTable 中增加 group 信息,还创立了 RecomposeScope 实例增加 SlotTable 中。

@ComposeCompilerApi
override fun startRestartGroup(key: Int): Composer {
 start(key, null, false, null)
 addRecomposeScope()
 return this
}
private fun addRecomposeScope() {
 if (inserting) {
 val scope = RecomposeScopeImpl(composition as CompositionImpl)
 invalidateStack.push(scope)
 updateValue(scope)
 scope.start(compositionToken)
 } else {
		//...
 }
}

咱们疏忽 group 信息的话,能够幻想成这个姿态

19.3 ComposeView 中的 Composition

internal class RecomposeScopeImpl(
 composition: CompositionImpl?
) : ScopeUpdateScope, RecomposeScope {
    var composition: CompositionImpl? = composition
        private set
    private var block: ((Composer, Int) -> Unit)? = null
    fun compose(composer: Composer) {
        block?.invoke(composer, 1) ?: error("Invalid restart scope")
    }  
    override fun invalidate() {
        composition?.invalidate(this, null)
    }
    override fun updateScope(block: (Composer, Int) -> Unit) { this.block = block }  
}  

源码能够看出 RecomposeScopeImpl 持有当时 @Composable 函数的引用 block ,一起 compose() 办法能够履行 block。

RecomposeScopeImpl 是重组的最小单元。

LayoutNode 的解析过程

@Composable 函数履行时当遇到 UI 相关部分时, 咱们那 Layout 举例

@Composable inline fun Layout(
    content: @Composable @UiComposable () -> Unit,
    modifier: Modifier = Modifier,
    measurePolicy: MeasurePolicy
) {
    val density = LocalDensity.current
    val layoutDirection = LocalLayoutDirection.current
    val viewConfiguration = LocalViewConfiguration.current
    ReusableComposeNode<ComposeUiNode, Applier<Any>>(
        factory = ComposeUiNode.Constructor,
        update = {
            set(measurePolicy, ComposeUiNode.SetMeasurePolicy)
            set(density, ComposeUiNode.SetDensity)
            set(layoutDirection, ComposeUiNode.SetLayoutDirection)
            set(viewConfiguration, ComposeUiNode.SetViewConfiguration)
        },
        skippableUpdate = materializerOf(modifier),
        content = content
    )
}
@Composable @ExplicitGroupsComposable
inline fun <T, reified E : Applier<*>> ReusableComposeNode(
    noinline factory: () -> T,
    update: @DisallowComposableCalls Updater<T>.() -> Unit,
    noinline skippableUpdate: @Composable SkippableUpdater<T>.() -> Unit,
    content: @Composable () -> Unit
) {
    if (currentComposer.applier !is E) invalidApplier()
      //composer.start 增加 group 信息
    currentComposer.startReusableNode()
    if (currentComposer.inserting) {
      	//factory = ComposeUiNode.Constructor
      	//调用 ComposeUiNode 结构函数创立 LayoutNode
      	//更新 group 信息
      	//运用 UiApplier 将 LayoutNode 增加到 AndroidComposeView.root 中
        currentComposer.createNode(factory)
    } else {
        currentComposer.useNode()
    }
    currentComposer.disableReusing()
     //履行 update {} 设置 measurePolicy density layoutDirection viewConfiguration
    Updater<T>(currentComposer).update()
    currentComposer.enableReusing()
    SkippableUpdater<T>(currentComposer).skippableUpdate()
    currentComposer.startReplaceableGroup(0x7ab4aae9)
    content()
    currentComposer.endReplaceableGroup()
    currentComposer.endNode()
}

本文没有深化细节,期望这样能够帮助大家对这些概念有个开始知道,再再再次引荐 探究 Jetpack Compose 内核:深化 SlotTable 体系 。

19.3 ComposeView 中的 Composition

  • Recomposer 提供运转环境

  • Composition 记录当时 Compose 运转状态

  • AndroidComposeView 担任显现当时状态下的 UI