上一章分析了 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