前言
其实对于Compose的原理,我在之前的文章中介绍过很多,像mutableStateOf
的订阅原理、Compose中的布局和绘制原理、LayoutNode
层级原理、Modifier
的初始化等等,这些对于后续Compose开发其实会有很大的帮助,因为懂得了原理,才能写出高质量的代码。
那么本节我将会带大家继续深入原理,从Compose全局出发认识底层不为人知的原理。
1 Compose - setContent 原理
当我们需要写一个Compose界面的时候,通常是在setContent
函数中进行组合函数的拼接,例如下面的代码:
kotlin
class MainActivity : ComponentActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContent {
// TODO show ui
}
}
}
这是系统默认帮我们生成的模板,当前Activity是继承自ComponentActivity
,那么继承自AppCompatActivity
或者FragmentActivity
可以吗?试过之后发现不可以,而且如果新建的项目中,连AppCompatActivity
的依赖都没有导入,可以说是完全摒弃了appcompat系列。
为什么只能继承ComponentActivity
呢?是因为setContent
内容显示是ComponentActivity
的一个扩展函数,在其他Activity中就无法调用并显示页面。
1.1 ComponentActivity # setContent源码分析
所以,我会带着大家看一下setContent
源码,看@Composable
函数如何一步一步显示在屏幕上。
kotlin
public fun ComponentActivity.setContent(
parent: CompositionContext? = null,
content: @Composable () -> Unit
) {
// 核心代码1:
val existingComposeView = window.decorView
.findViewById<ViewGroup>(android.R.id.content)
.getChildAt(0) as? ComposeView
// 核心代码2:
if (existingComposeView != null) with(existingComposeView) {
setParentCompositionContext(parent)
setContent(content)
} else ComposeView(this).apply {
// 核心代码3:
// Set content and parent **before** setContentView
// to have ComposeView create the composition on attach
setParentCompositionContext(parent)
setContent(content)
// Set the view tree owners before setting the content view so that the inflation process
// and attach listeners will see them already present
setOwners()
setContentView(this, DefaultActivityContentLayoutParams)
}
}
核心代码1:
熟悉窗口伙伴应该了解,其分层结构如下:
之前在使用AppCompactActivity
的时候,一定会调用setContentView
方法,将布局添加到窗口上。
java
@Override
public void setContentView(View v) {
ensureSubDecor();
ViewGroup contentParent = mSubDecor.findViewById(android.R.id.content);
contentParent.removeAllViews();
contentParent.addView(v);
mAppCompatWindowCallback.getWrapped().onContentChanged();
}
在setContent
函数中,首先会从android.R.id.content
中查找第一个子View,看是否为ComposeView
,在上一节中我讲过,ComposeView
是在Android原生的View体系中融入Compose UI,利用其内部提供的setContent
显示组合函数。
如果没有设置过setContentView
,或者第一个子View不是ComposeView
类型,那么existingComposeView
就为空;否则就不为空。
核心代码2:
如果不为空,那么就直接调用ComposeView
的setContent
和setParentCompositionContext
函数,将组合函数传递进去。
核心代码3:
如果为空,那么就自行创建一个ComposeView
,也会调用ComposeView
的setContent
和setParentCompositionContext
函数,最终调用setContentView
将ComposeView
添加到android.R.id.content
容器中。
所以setContent
的核心逻辑就是判断是否调用过setContentView
函数,如果没有调用过那么就自行创建一个ComposeView
,添加到窗口上。
如果调用过setContentView
,而且第一个子组件是ComposeView
,那么调用setContent
函数就是直接在这个子组件中添加UI元素。
kotlin
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:orientation="vertical"
android:layout_width="match_parent"
android:layout_height="match_parent">
<androidx.compose.ui.platform.ComposeView
android:id="@+id/composeview"
android:layout_width="match_parent"
android:layout_height="wrap_content"/>
</LinearLayout>
如果调用过setContentView
,而且第一个子组件不是ComposeView
,那么会重新创建ComposeView
并覆盖之前调用setContentView
的内容。
kotlin
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:orientation="vertical"
android:layout_width="match_parent"
android:layout_height="match_parent">
<ImageView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:src="@drawable/ic_launcher_background"/>
<androidx.compose.ui.platform.ComposeView
android:id="@+id/composeview"
android:layout_width="match_parent"
android:layout_height="wrap_content"/>
</LinearLayout>
例如上面这种布局,ImageView
将会被覆盖不会显示在屏幕上,所以需要在写布局的时候注意一下,调换一下位置,ImageView
就会正常显示出来。
1.2 ComposeView # setContent源码分析
这个是当我们创建ComposeView
之后,想要显示内容必须调用的函数。
kotlin
// ComposeView.android.kt
/**
* Set the Jetpack Compose UI content for this view.
* Initial composition will occur when the view becomes attached to a window or when
* [createComposition] is called, whichever comes first.
*/
fun setContent(content: @Composable () -> Unit) {
shouldCreateCompositionOnAttachedToWindow = true
this.content.value = content
if (isAttachedToWindow) {
createComposition()
}
}
这个函数很简单,首先给shouldCreateCompositionOnAttachedToWindow
设置为了true,然后存储传入的content
,是通过mutableStateOf
来存储的。
最后判断当前View是否与Window绑定,因为这个调用时机在onCreate
,真正与窗口绑定是在调用了WindowManager
的addView
之后,这个过程是发生在onResume
时,因此这个时机是没有绑定的,所以不会执行createComposition
函数。
因为ComposeView
是传统的View组件,所以当它被添加到窗口的时候,会执行onAttachedToWindow
方法,这个是在ComposeView
的父类AbstractComposeView
重写的,它是一个ViewGroup
。
kotlin
override fun onAttachedToWindow() {
super.onAttachedToWindow()
previousAttachedWindowToken = windowToken
// 在setContent函数调用时,将其赋值为true
if (shouldCreateCompositionOnAttachedToWindow) {
ensureCompositionCreated()
}
}
因为在setContent
函数调用的时候,将shouldCreateCompositionOnAttachedToWindow
赋值为true,因此在View添加到窗口时,会执行ensureCompositionCreated
函数。
1.2.1 AbstractComposeView # ensureCompositionCreated
在初始组合的过程中,composition
一定为空,因此会进入到初始化的过程中。
kotlin
private var composition: Composition? = null
@Suppress("DEPRECATION") // Still using ViewGroup.setContent for now
private fun ensureCompositionCreated() {
if (composition == null) {
try {
creatingComposition = true
composition = setContent(resolveParentCompositionContext()) {
Content()
}
} finally {
creatingComposition = false
}
}
}
首先,我们先看下Composition
是什么?你可以认为它就是用来描述界面的,是由所有的@Composable
函数组成的,类似于一个ViewTree
。
所以当View和窗口绑定之后,需要做的一件事就是确保Composition
是否已经创建了,会执行AbstarctComposeView
的扩展函数setContent
。
kotlin
internal fun AbstractComposeView.setContent(
parent: CompositionContext,
content: @Composable () -> Unit
): Composition {
GlobalSnapshotManager.ensureStarted()
val composeView =
if (childCount > 0) {
getChildAt(0) as? AndroidComposeView
} else {
removeAllViews(); null
} ?: AndroidComposeView(context).also { addView(it.view, DefaultLayoutParams) }
return doSetContent(composeView, parent, content)
}
1.2.3 AbstractComposeView # setContent
在这个函数中,第一个参数是CompositionContext
,它是通过resolveParentCompositionContext
函数创建的,其实最终返回的是一个Recomposer
对象。
kotlin
/**
* The scheduler for performing recomposition and applying updates to one or more [Composition]s.
*/
@Suppress("RedundantVisibilityModifier", "NotCloseable")
@OptIn(InternalComposeApi::class)
class Recomposer(
effectCoroutineContext: CoroutineContext
) : CompositionContext() {
// ......
}
Recomposer
是用来重组界面的,当界面的数据(一般指mutableStateOf
)发生变化时,调用这个数据所在的ComposeScope
会进行重组,此时就是通过Recomposer
来完成。
第二个参数content
,其实就是在调用ComposeView
的setContent
函数时传入的一系列组合函数,最终调用AbstractComposeView
的setContent
函数来创建Composition
。
1.3 State刷新底层原理
前面我介绍了AbstractComposeView
的setContent
函数中的两个参数,再回头看里面的实现。
kotlin
internal fun AbstractComposeView.setContent(
parent: CompositionContext,
content: @Composable () -> Unit
): Composition {
// 核心代码1:
GlobalSnapshotManager.ensureStarted()
// 核心代码2:
val composeView =
if (childCount > 0) {
getChildAt(0) as? AndroidComposeView
} else {
removeAllViews(); null
} ?: AndroidComposeView(context).also { addView(it.view, DefaultLayoutParams) }
return doSetContent(composeView, parent, content)
}
1.3.1 观察者注册
首先调用了GlobalSnapshotManager
的ensureStarted
函数,我先说下这个流程的作用:就是能够感知Compose中的State值的变化,通过ReComposer
触发重组,来刷新页面。
kotlin
internal object GlobalSnapshotManager {
private val started = AtomicBoolean(false)
fun ensureStarted() {
if (started.compareAndSet(false, true)) {
val channel = Channel<Unit>(Channel.CONFLATED)
CoroutineScope(AndroidUiDispatcher.Main).launch {
channel.consumeEach {
Snapshot.sendApplyNotifications()
}
}
Snapshot.registerGlobalWriteObserver {
channel.trySend(Unit)
}
}
}
}
首先创建了一个Channel
,如果不熟悉Channel
的伙伴可以看下我之前的文章,它其实是属于热流的一种,能够完成协程之间的通信。
首先是调用Snapshot
的registerGlobalWriteObserver
函数,这里是注册了一个全局的写函数观察者。
kotlin
fun registerGlobalWriteObserver(observer: ((Any) -> Unit)): ObserverHandle {
sync {
// globalWriteObservers是一个集合,会把observer添加到globalWriteObservers
globalWriteObservers += observer
}
advanceGlobalSnapshot()
return ObserverHandle {
sync {
globalWriteObservers -= observer
}
advanceGlobalSnapshot()
}
}
看过我前面文章的伙伴们应该知道,当Compose中的State变量进行读写操作时,Snapshot
中的读写观察者会做相应的处理操作,我再带伙伴们回顾一下,这里很重要,会跟之前的知识串联起来。
当State的值发生变化时,会调用set(value)
,执行StateRecord
的overwritable
函数对值进行复写。
kotlin
internal open class SnapshotMutableStateImpl<T>(
value: T,
override val policy: SnapshotMutationPolicy<T>
) : StateObjectImpl(), SnapshotMutableState<T> {
@Suppress("UNCHECKED_CAST")
override var value: T
get() = next.readable(this).value
set(value) = next.withCurrent {
if (!policy.equivalent(it.value, value)) {
next.overwritable(this, it) { this.value = value }
}
}
// ......
}
重点看下面这个函数overwritable
。
kotlin
internal inline fun <T : StateRecord, R> T.overwritable(
state: StateObject,
candidate: T,
block: T.() -> R
): R {
var snapshot: Snapshot = snapshotInitializer
return sync {
snapshot = Snapshot.current
this.overwritableRecord(state, snapshot, candidate).block()
}.also {
notifyWrite(snapshot, state)
}
}
在这个函数中拿到的Snapshot
是GlobalSnapshot
,它是通过AtomicReference
修饰,说明会有高并发的场景。
kotlin
@PublishedApi
internal val snapshotInitializer: Snapshot = currentGlobalSnapshot.get()
private val currentGlobalSnapshot = AtomicReference(
GlobalSnapshot(
id = nextSnapshotId++,
invalid = SnapshotIdSet.EMPTY
).also {
openSnapshots = openSnapshots.set(it.id)
}
)
GlobalSnapshot
是继承自MutableSnapshot
,它构造函数第4个参数,就是writeObserver
,也就是在registerGlobalWriteObserver
函数调用时,传入的观察者。
kotlin
internal class GlobalSnapshot(id: Int, invalid: SnapshotIdSet) :
MutableSnapshot(
id, invalid, null, //readObserver
sync {
globalWriteObservers.let {
it.singleOrNull() ?: { state: Any ->
it.fastForEach { it(state) }
}
}
} // writeObserver
)
当State的值发生变化之后,会调用notifyWrite
函数,从源码中看,就是拿到了MutableSnapshot
中的writeObserver
,然后执行其回调。
kotlin
@PublishedApi
internal fun notifyWrite(snapshot: Snapshot, state: StateObject) {
snapshot.writeCount += 1
snapshot.writeObserver?.invoke(state)
}
那么再回头看,其实globalWriteObservers
的回调,就是执行了channel
的trySend
函数,也就是说当State的值发生变化时,会通过Channel
把对应的状态发送,然后在consumeEach
中会接收到。
1.3.2 页面刷新
当State的值发生变化时,会通过Channel
发送通知,当在consumeEach
接收到通知后,会执行Snapshot
的sendApplyNotifications
函数。
kotlin
fun sendApplyNotifications() {
val changes = sync {
currentGlobalSnapshot.get().modified?.isNotEmpty() == true
}
if (changes)
advanceGlobalSnapshot()
}
在这个函数中,首先会判断是否真的有值发生了变化,如果没有就不需要处理;如果真的发生了变化,那么就会执行advanceGlobalSnapshot
函数。
kotlin
private fun <T> advanceGlobalSnapshot(block: (invalid: SnapshotIdSet) -> T): T {
var previousGlobalSnapshot = snapshotInitializer as GlobalSnapshot
var modified: IdentityArraySet<StateObject>? = null // Effectively val; can be with contracts
// 核心代码1:
val result = sync {
previousGlobalSnapshot = currentGlobalSnapshot.get()
modified = previousGlobalSnapshot.modified
if (modified != null) {
pendingApplyObserverCount.add(1)
}
takeNewGlobalSnapshot(previousGlobalSnapshot, block)
}
// If the previous global snapshot had any modified states then notify the registered apply
// observers.
// 核心代码2:
modified?.let {
try {
val observers = applyObservers
observers.fastForEach { observer ->
observer(it, previousGlobalSnapshot)
}
} finally {
pendingApplyObserverCount.add(-1)
}
}
sync {
checkAndOverwriteUnusedRecordsLocked()
modified?.fastForEach { processForUnusedRecordsLocked(it) }
}
return result
}
核心代码1:
这里面的代码我就不跟进了,其实就是当前Snapshot
中使用过【这些发生改变的变量】的地方失效,并构建一个新的Snapshot
。
核心代码2:
遍历applyObservers
,并执行observer
回调。那么applyObservers
是什么时候初始化的呢?
kotlin
fun registerApplyObserver(observer: (Set<Any>, Snapshot) -> Unit): ObserverHandle {
// Ensure observer does not see changes before this call.
advanceGlobalSnapshot(emptyLambda)
sync {
applyObservers += observer
}
return ObserverHandle {
sync {
applyObservers -= observer
}
}
}
在registerApplyObserver
函数中,会将observer
添加到applyObservers
中,而这个函数的调用是在Composer
的recompositionRunner
函数中。
kotlin
@OptIn(ExperimentalComposeApi::class)
private suspend fun recompositionRunner(
block: suspend CoroutineScope.(parentFrameClock: MonotonicFrameClock) -> Unit
) {
val parentFrameClock = coroutineContext.monotonicFrameClock
withContext(broadcastFrameClock) {
// ......
// Observe snapshot changes and propagate them to known composers only from
// this caller's dispatcher, never working with the same composer in parallel.
// unregisterApplyObserver is called as part of the big finally below
val unregisterApplyObserver = Snapshot.registerApplyObserver { changed, _ ->
synchronized(stateLock) {
if (_state.value >= State.Idle) {
changed.fastForEach {
if (
it is StateObjectImpl &&
!it.isReadIn(ReaderKind.Composition)
) {
// continue if we know that state is never read in composition
return@fastForEach
}
snapshotInvalidations.add(it)
}
deriveStateLocked()
} else null
}?.resume(Unit)
}
// ......
}
}
当observer
执行时,会将修改过的StateObject
组合,也就是所有的变量集合传入并遍历,在遍历的过程中,如果当前变量虽然被修改了,但是不会在组合中使用,就会跳过。
然后将发生变化的StateObject
放在snapshotInvalidations
失效集合中,遍历完成之后,执行resume
将Composer挂起的协程唤醒,触发重组。
所以在创建Composition
之前,会先进行State状态变化监听的初始化工作。
1.4 Composition构建
OK,再回到AbstractComposeView
的setContent
函数,看Composition
是如何创建的。
因为AbstractComposeView
是一个ViewGroup
,因此会判断其中第一个子组件是否为AndroidComposeView
;如果不是,那么就会创建一个AndroidComposeView
添加到AbstractComposeView
当中。
然后执行doSetContent
函数。
kotlin
private fun doSetContent(
owner: AndroidComposeView,
parent: CompositionContext,
content: @Composable () -> Unit
): Composition {
// .....
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
}
首先会创建一个Composition
对象,其实从源码看就是CompositionImpl
;
kotlin
fun Composition(
applier: Applier<*>,
parent: CompositionContext
): Composition =
CompositionImpl(
parent,
applier
)
然后取以R.id.wrapped_composition_tag
为Tag的View,因为第一次进来肯定为空,所以会创建一个WrappedComposition
对象,并把owner
和original
传递进去,调用了WrappedComposition
的setContent
函数,其实最终创建的是一个WrappedComposition
。
kotlin
// Wrapper.android.kt
override fun setContent(content: @Composable () -> Unit) {
owner.setOnViewTreeOwnersAvailable {
if (!disposed) {
val lifecycle = it.lifecycleOwner.lifecycle
lastContent = content
if (addedToLifecycle == null) {
addedToLifecycle = lifecycle
// this will call ON_CREATE synchronously if we already created
lifecycle.addObserver(this)
} else if (lifecycle.currentState.isAtLeast(Lifecycle.State.CREATED)) {
// 内部又是一个setContent
original.setContent {
@Suppress("UNCHECKED_CAST")
val inspectionTable =
owner.getTag(R.id.inspection_slot_table_set) as?
MutableSet<CompositionData>
?: (owner.parent as? View)?.getTag(R.id.inspection_slot_table_set)
as? MutableSet<CompositionData>
if (inspectionTable != null) {
inspectionTable.add(currentComposer.compositionData)
currentComposer.collectParameterInformation()
}
LaunchedEffect(owner) { owner.boundsUpdatesEventLoop() }
CompositionLocalProvider(LocalInspectionTables provides inspectionTable) {
ProvideAndroidCompositionLocals(owner, content)
}
}
}
}
}
}
我们看到在内部又调用了original
的setContent
函数,其实就是调用了CompositionImpl
的setContent
函数,内部调用composeInitial
。
kotlin
// Composition.kt
override fun setContent(content: @Composable () -> Unit) {
composeInitial(content)
}
private fun composeInitial(content: @Composable () -> Unit) {
check(!disposed) { "The composition is disposed" }
this.composable = content
parent.composeInitial(this, composable)
}
从字面意思上看,就是对Compose进行初始化。parent
就是ReComposer
,可以看下再回顾一下前面的代码,CompositionContext
其实就是ReComposer
。
kotlin
// ReComposer.kt
internal override fun composeInitial(
composition: ControlledComposition,
content: @Composable () -> Unit
) {
val composerWasComposing = composition.isComposing
try {
// 核心代码:
composing(composition, null) {
composition.composeContent(content)
}
} catch (e: Exception) {
processCompositionError(e, composition, recoverable = true)
return
}
// .....
}
当执行composeInitial
函数时,内部调用了composing
函数,这个函数最终执行了lambda表达式中的代码,最终还是调用了CompositionImpl
的composeContent
函数。
kotlin
override fun composeContent(content: @Composable () -> Unit) {
// TODO: This should raise a signal to any currently running recompose calls
// to halt and return
guardChanges {
synchronized(lock) {
drainPendingModificationsForCompositionLocked()
guardInvalidationsLocked { invalidations ->
val observer = observer()
@Suppress("UNCHECKED_CAST")
observer?.onBeginComposition(
this,
invalidations.asMap() as Map<RecomposeScope, Set<Any>?>
)
// Composer.xx
composer.composeContent(invalidations, content)
observer?.onEndComposition(this)
}
}
}
}
层层套,各种套娃......最终执行了Composer
的composeContent
函数。
kotlin
// Composer.kt
internal fun composeContent(
invalidationsRequested: IdentityArrayMap<RecomposeScopeImpl, IdentityArraySet<Any>?>,
content: @Composable () -> Unit
) {
runtimeCheck(changes.isEmpty()) { "Expected applyChanges() to have been called" }
doCompose(invalidationsRequested, content)
}
kotlin
// Composer.kt
private fun doCompose(
invalidationsRequested: IdentityArrayMap<RecomposeScopeImpl, IdentityArraySet<Any>?>,
content: (@Composable () -> Unit)?
) {
runtimeCheck(!isComposing) { "Reentrant composition is not supported" }
trace("Compose:recompose") {
try {
// ......
observeDerivedStateRecalculations(derivedStateObserver) {
if (content != null) {
startGroup(invocationKey, invocation)
invokeComposable(this, content)
endGroup()
} else if (
(forciblyRecompose || providersInvalid) &&
savedContent != null &&
savedContent != Composer.Empty
) {
startGroup(invocationKey, invocation)
@Suppress("UNCHECKED_CAST")
invokeComposable(this, savedContent as @Composable () -> Unit)
endGroup()
} else {
skipCurrentGroup()
}
}
endRoot()
complete = true
} finally {
isComposing = false
invalidations.clear()
if (!complete) abortRoot()
createFreshInsertTable()
}
}
}
最终是执行了Composer
的invokeComposable
函数,走到这里,才是真正执行到了setContent
函数内部的代码逻辑。这么一看,好像setContent
一点事儿也没做,倒不是一点儿没做,是跟页面的显示没有半点关系 ,主要做了一些初始化的任务,例如设置State状态的监听,通知Recomposer
刷新页面等。
java
public static final void invokeComposable(@NotNull Composer composer, @NotNull Function2 composable) {
Intrinsics.checkNotNull(composable, "null cannot be cast to non-null type kotlin.Function2<androidx.compose.runtime.Composer, kotlin.Int, kotlin.Unit>");
Function2 realFn = (Function2)TypeIntrinsics.beforeCheckcastToFunctionOfArity(composable, 2);
realFn.invoke(composer, 1);
}
来看下setContent
中的@Composable
函数是如何执行的,在这里Compose编译器插件是会将@Composable
函数转化为了一个Function2
函数,然后执行了这个函数。
1.5 小结
来,通过一个流程图来看下setContent
的主要逻辑:
2 Compose UI显示原理
前面,我介绍了setContent
的原理,其实setContent
中更多起一些辅助的作用,例如注册State修改的监听,触发Recomposer的重组,以及提供执行@Composable
函数的入口等,那么真正显示的逻辑其实还是在setContent
内部的@Composable
函数中进行的。
kotlin
setContent {
Text(text = "1122222")
}
对于Compose当中的组件,无论是容器,还是单一的View,最终都会执行Layout
函数,在Layout
函数中,会创建ReusableComposeNode
。
kotlin
inline fun Layout(
modifier: Modifier = Modifier,
measurePolicy: MeasurePolicy
) {
val density = LocalDensity.current
val layoutDirection = LocalLayoutDirection.current
val viewConfiguration = LocalViewConfiguration.current
val materialized = currentComposer.materialize(modifier)
ReusableComposeNode<ComposeUiNode, Applier<Any>>(
factory = ComposeUiNode.Constructor,
update = {
set(measurePolicy, ComposeUiNode.SetMeasurePolicy)
set(density, ComposeUiNode.SetDensity)
set(layoutDirection, ComposeUiNode.SetLayoutDirection)
set(viewConfiguration, ComposeUiNode.SetViewConfiguration)
set(materialized, ComposeUiNode.SetModifier)
},
)
}
在ReusableComposeNode
内部,会通过Composer
执行createNode
函数,进行节点的创建,也就是执行factory
内部的表达式。
kotlin
@Suppress("NONREADONLY_CALL_IN_READONLY_COMPOSABLE", "UnnecessaryLambdaCreation")
@Composable inline fun <T : Any, reified E : Applier<*>> ReusableComposeNode(
noinline factory: () -> T,
update: @DisallowComposableCalls Updater<T>.() -> Unit
) {
if (currentComposer.applier !is E) invalidApplier()
currentComposer.startReusableNode()
if (currentComposer.inserting) {
currentComposer.createNode { factory() }
} else {
currentComposer.useNode()
}
Updater<T>(currentComposer).update()
currentComposer.endNode()
}
回到上头看一下,factory
中执行的是ComposeUiNode.Constructor
,
kotlin
val Constructor: () -> ComposeUiNode = LayoutNode.Constructor
// 这里直接new出一个LayoutNode
internal val Constructor: () -> LayoutNode = { LayoutNode() }
其实就是创建了一个LayoutNode
,也就是之前我在讲LayoutModifier
时提到的,如此整个知识链路就串起来了。
所以在setContent
内部的全部UI元素,都被一一转换为了LayoutNode
,并组装成LayoutNode
树。如果之前不知道Compose当中是在什么时候将@Composable
函数转换为LayoutNode
,看了这节的知识就应该知道了。