上一章剖析了 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 中:
-
多了一个 AndroidComposeView 类型的子 View
-
ComposeView.composition 特点被赋值成 WrappedComposition 目标
AndroidComposeView 是真正担任显现 UI 的组件。
Composition 担任保存 @Composable content 函数解析后的一切信息 (位置、状态、运转环境、函数体本身等) 。
Composition 中保存的数据与 AndroidComposeView 中显现的 UI 是对应联系。能够理解成他们是 ComposeView中的 State – UI 。
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.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)
最后会履行 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 信息的话,能够幻想成这个姿态
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 体系 。
-
Recomposer 提供运转环境
-
Composition 记录当时 Compose 运转状态
-
AndroidComposeView 担任显现当时状态下的 UI