Jetpack Compose setContent 源码分析
从何入手
先来了解一下Compose架构的分层设计
由上至下 | 说明 | 运用 |
---|---|---|
material | 提供了Material Design一套风格体系,包含主题系统、样式化组件 | Button、AlertDialog等等 |
foundation | 相当于面向开发者的跟基层,包含完整的UI系统和实用布局 | LazyList、Row、Column等等 |
animation | 动画层,包含平移、渐变、缩放等等,并且提供了方便开发者的动画组件 | animate**AsState、Transition、Animatable、AnimatedVisibility等等 |
ui | ui相关的基础功能,包括自定义布局、绘制、触摸反馈等等 | ComposeView、Layout、LayoutNode等等 |
runtime | 最底层的概念模型,包括数据结构、状态处理、数据同步等等 | mutableStateOf、remember等等 |
compiler | 基于Kotlin的编译器插件 | 处理@Composable函数 |
了解完整体的分层设计,而要分析的setContent()
源码是处于ui
层和runtime
层。
Compose版本号采用
1.0.1
代码块顶部注释为类位置
先看一段Compose代码
class MainActivity : ComponentActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
// 入口函数
setContent { // content 函数对象
Text("Hello Compose")
}
}
}
写法很简单,所以我们的目的也很简单,就是看看setContent()
函数讲Text("Hello Compose")
进行怎么样的逻辑传递。为了之后的代码跟随能够更加的清晰,后续讲到入口函数对象
就等同于Text("Hello Compose")
ComponentActivity.setContent()
// androidx.activity.compose.ComponentActivity
public fun ComponentActivity.setContent(
parent: CompositionContext? = null,
content: @Composable () -> Unit
) {
// 1. 通过decorView找到ContentView,再获得第一个ComposeView
val existingComposeView = window.decorView
.findViewById(android.R.id.content)
.getChildAt(0) as? ComposeView
// 2. 如果ComposeView为空则走初始化流程
if (existingComposeView != null) with(existingComposeView) {
setParentCompositionContext(parent)
setContent(content)
} else ComposeView(this).apply {
// 3. 初始化ComposeView肯定为空,则进入这边
setParentCompositionContext(parent)
// 4. 把入口函数对象传入ComposeView
setContent(content)
setOwners()
// 5. 把ComposeView设置进ContentView
setContentView(this, DefaultActivityContentLayoutParams)
}
}
从第一步可以了解到,原来在当前窗口的decorView中的android.R.id.content
中第0
个位置,会存在一个ComposeView,所以ComposeView结构图可以理解成:
难道这个ComposeView肯定跟传统的View/ViewGroup有关系吗?遇事不决就看看Compose的继承关系。
果真如此,也能验证Compose架构的分层设计中上下关系,animation
、foundation
的工作也是在ComposeView中进行,另外还能延伸出一个问题,那是不是Compose和传统View/ViewGroup能够互相交互并且能在同一个activity混合开发?这个目前没有研究过,就先略过了。。。
第二步的话,由于分析就是初始化状态的流程且existingComposeView肯定为空,所以直接进入到第三步,传递了parent
到setParentCompositionContext
方法,按照案例的入口函数,
// 入口函数
setContent(parent = null) { // content 函数对象
Text("Hello Compose")
}
所以当前parent
为空,再看看方法里具体做了哪些。
// androidx.compose.ui.platform.AbstractComposeView
fun setParentCompositionContext(parent: CompositionContext?) {
parentContext = parent
}
只是赋值操作,目前parentContext
为空
第四步发现又调用了一个相同方法名的setContent
,在之后的分析还会出现一个类似的setContent
,每一个方法作用都是不一样,那看看当前ComposeView.setContent(入口函数对象)
做了什么事情
// androidx.compose.ui.platform.ComposeView
class ComposeView @JvmOverloads constructor(
context: Context,
attrs: AttributeSet? = null,
defStyleAttr: Int = 0
) : AbstractComposeView(context, attrs, defStyleAttr) {
private val content = mutableStateOf<(@Composable () -> Unit)?>(null)
@Suppress("RedundantVisibilityModifier")
protected override var shouldCreateCompositionOnAttachedToWindow: Boolean = false
private set
@Composable
override fun Content() {
content.value?.invoke()
}
fun setContent(content: @Composable () -> Unit) {
// 翻译为 “应该在附加到窗口上创建合成”,做一个标记
shouldCreateCompositionOnAttachedToWindow = true
// 赋值操作
this.content.value = content
if (isAttachedToWindow) {
// 暂时不会进入
createComposition()
}
}
}
整个ComposeView的类结构
AbstractComposeView
abstract fun Content()
open val shouldCreateCompositionOnAttachedToWindow: Boolean
ComposeView
val content = mutableStateOf
fun setContent()
这样一看理解ComposeView也很容易了,它只是做了一个预备动作,告诉AbstractComposeView有人(调用ComposeView.setContent()
后)把预备状态修改成准备就绪啦(shouldCreateCompositionOnAttachedToWindow = true
),并且入口函数对象也存储好了,等你来拿走处理了(Content()
= 入口函数对象)。
所以前两句就是给AbstractComposeView
修改和准备需要的值。
而if
对于初始化分析来说,根本不会进入,因为当前的ComponentActivity.setContent
,还没用执行到第五步setContentView
,所以isAttachedToWindow
肯定为false。
那再看第五步,setContentView
再熟悉不过了,添加到android.R.id.content
中。之后流程居然就没了?第四步都准备就绪了,之后就没有启动逻辑了?
回想一下之前讲的ComposeView的继承关系,是View的子类,那setContentView
后ComposeView被附加到Window上后,会回调方法onAttachedToWindow()
。我们知道ComposeView的没有这个方法的,在父类AbstractComposeView
中找到了该实现。
AbstractComposeView.onAttachedToWindow()
// androidx.compose.ui.platform.AbstractComposeView
override fun onAttachedToWindow() {
super.onAttachedToWindow()
previousAttachedWindowToken = windowToken
// ComposeView 修改后预备状态
if (shouldCreateCompositionOnAttachedToWindow) {
// 真正启动的地方
ensureCompositionCreated()
}
}
shouldCreateCompositionOnAttachedToWindow
这个值Compose.setContent()
已经修改为true
,所以直接看ensureCompositionCreated()
// androidx.compose.ui.platform.AbstractComposeView
private fun ensureCompositionCreated() {
// composition 初始化流程为空
if (composition == null) {
try {
creatingComposition = true
// 又来一个 setContent?
composition = setContent(resolveParentCompositionContext()) {
// 抽象方法
Content()
}
} finally {
creatingComposition = false
}
}
}
composition
还不确定是做什么的,先一步一步解析这个setContent
涉及的内容,看看方法的入参。
// androidx.compose.ui.platform.Wrapper_androidKt.class
internal fun ViewGroup.setContent(
parent: CompositionContext,
content: @Composable () -> Unit
): Composition {
...
parent
以目前的知识点也无法对它进行分析,而content
就是我们的入口函数对象。调用的Content()
抽象方法,方法的实现之前讲过的,内部返回的就是ComposeView.content.value
为了了解parent
为何物,就要看看resolveParentCompositionContext()
方法。
resolveParentCompositionContext()
// androidx.compose.ui.platform.AbstractComposeView
private fun resolveParentCompositionContext() = parentContext
?: findViewTreeCompositionContext()?.also { cachedViewTreeCompositionContext = it }
?: cachedViewTreeCompositionContext
?: windowRecomposer.also { cachedViewTreeCompositionContext = it }
一大堆判空逻辑,那就挨个分析。
parentContext
: 就是ComponentActivity.setContent()
中第三步流程,并且还知道当前案例传递为空。
findViewTreeCompositionContext()
: 再看看源码怎么寻找
// androidx.compose.ui.platform.WindowRecomposer_androidKt.class
fun View.findViewTreeCompositionContext(): CompositionContext? {
var found: CompositionContext? = compositionContext
if (found != null) return found
var parent: ViewParent? = parent
while (found == null && parent is View) {
found = parent.compositionContext
parent = parent.getParent()
}
return found
}
初始化流程默认compositionContext
为空,所以找上一层 parentVew, 然而整个while也是找不到需要的compositionContext
,所以findViewTreeCompositionContext
也会返回null。
cachedViewTreeCompositionContext
: 是跟随上一个findViewTreeCompositionContext()
逻辑走的,上一个为空则cachedViewTreeCompositionContext
也会返回空。
windowRecomposer
: 看看源码怎么寻找
// androidx.compose.ui.platform.WindowRecomposer_androidKt.class
internal val View.windowRecomposer: Recomposer
get() {
check(isAttachedToWindow) {
"Cannot locate windowRecomposer; View $this is not attached to a window"
}
// 扩展函数拿到 contentChild
val rootView = contentChild
return when (val rootParentRef = rootView.compositionContext) {
// 初始化流程为null
null -> WindowRecomposerPolicy.createAndInstallWindowRecomposer(rootView)
is Recomposer -> rootParentRef
else -> error("root viewTreeParentCompositionContext is not a Recomposer")
}
}
private val View.contentChild: View
get() {
var self: View = this
var parent: ViewParent? = self.parent
while (parent is View) {
// 拿到 ComposeView 后返回
if (parent.id == android.R.id.content) return self
self = parent
parent = self.parent
}
return self
}
contentChild
: View的扩展属性,当前view的parentId等于android.R.id.content
时,会返回当前view。根据之前的知识(ComposeView结构图)就可以得知,当前view肯定是初始化setContentView
传入的ComposeView。
再根据当前为初始化流程,所以rootView.compositionContext
肯定也是为空,会进入WindowRecomposerPolicy.createAndInstallWindowRecomposer(rootView)
。看到这边整个流程都是在寻找compositionContext,但是第一次进入页面发现各种方法加扩展属性寻找,都压根找不到。从方法名create***
,其实就可以知道找不到我就去给你创建一个,并且这个方法还把rootView(ComposeView)传入,在还没有看createAndInstallWindowRecomposer(rootView)
,其实也可以猜出来,创建compositionContext是肯定的,并且还会把创建的compositionContext存储到rootView(ComposeView)其中,之后再次寻找就有缓存可寻。
WindowRecomposerPolicy.createAndInstallWindowRecomposer(rootView)
删减了部分代码
// androidx.compose.ui.platform.WindowRecomposer_androidKt.class
fun interface WindowRecomposerFactory {
fun createRecomposer(windowRootView: View): Recomposer
companion object {
val LifecycleAware: WindowRecomposerFactory = WindowRecomposerFactory { rootView ->
// 4. createRecomposer() lambda调用createLifecycleAwareViewTreeRecomposer()
rootView.createLifecycleAwareViewTreeRecomposer()
}
}
}
private fun View.createLifecycleAwareViewTreeRecomposer(): Recomposer {
...
// 5. 创建 Recomposer
val recomposer = Recomposer(contextWithClock)
...
// 6. 返回 Recomposer
return recomposer
}
object WindowRecomposerPolicy {
private val factory = AtomicReference(
// 2. 创建 WindowRecomposerFactory
WindowRecomposerFactory.LifecycleAware
)
...
// rootView = ComposeView
internal fun createAndInstallWindowRecomposer(rootView: View): Recomposer {
// 1. factory.get() 创建 WindowRecomposerFactory
// 3. createRecomposer(rootView) 调用 WindowRecomposerFactory.createRecomposer()
val newRecomposer = factory.get().createRecomposer(rootView)
// 7. compositionContext 赋值到 ComposeView Tag数组中
rootView.compositionContext = newRecomposer
...
// 8. 整个创建 compositionContext 结束
return newRecomposer
}
}
第一步到第三步是用工厂模式WindowRecomposerFactory
来创建Recomposer
。回想一下我们不是要拿CompositionContext对象的么?那这个Recomposer是何方神圣?
看一下继承关系
对于Recomposer怎么理解,官方注解如下
/**
* The scheduler for performing recomposition and applying updates to one or more [Composition]s.
* 用于执行重组并将更新应用到一个或多个 [Composition] 的调度程序。
* 先了解定义,之后会分析Recomposer的内部运用
*/
所以第四步到第六步就是创建CompositionContext,并且返回赋值给对象newRecomposer。
第七步是newRecomposer赋值给rootView.compositionContext
, 看看赋值过程。
// androidx.compose.ui.platform.WindowRecomposer_androidKt.class
var View.compositionContext: CompositionContext?
get() = getTag(R.id.androidx_compose_ui_view_composition_context) as? CompositionContext
set(value) {
setTag(R.id.androidx_compose_ui_view_composition_context, value)
}
就是把CompositionContext添加到ComposeView的Tag数组,在Compose ui层的体系中,发现有很多类似的写法,通过set/get Tag来存取值,且之后要分析的流程也有类似写法。
第八步整个获取CompositionContext流程终于结束了。
ViewGroup.setContent()
这个时候就要再贴一下之前的流程分析。
// androidx.compose.ui.platform.AbstractComposeView
private fun ensureCompositionCreated() {
// composition 初始化流程为空
if (composition == null) {
try {
creatingComposition = true
// 又来一个 setContent
composition = setContent(resolveParentCompositionContext()) {
// 抽象方法
Content()
}
} finally {
creatingComposition = false
}
}
}
// androidx.compose.ui.platform.Wrapper_androidKt.class
internal fun ViewGroup.setContent(
parent: CompositionContext, // Recomposer
content: @Composable () -> Unit // 入口函数对象
): Composition {
GlobalSnapshotManager.ensureStarted()
// 获得 AndroidComposeView
val composeView =
if (childCount > 0) {
getChildAt(0) as? AndroidComposeView
} else {
removeAllViews(); null
} ?: AndroidComposeView(context).also {
// 添加到 ViewGroup(ComposeView)
addView(it.view, DefaultLayoutParams)
}
// 又来一个doSetContent
return doSetContent(composeView, parent, content)
}
ViewGroup.setContent
方法很简单,获得AndroidComposeView添加到ComposeView,然后再调用doSetContent()。
以初始化流程来看肯定会创建AndroidComposeView,AndroidComposeView的结构图就可以理解为:
// // androidx.compose.ui.platform.Wrapper_androidKt.class
private fun doSetContent(
owner: AndroidComposeView,
parent: CompositionContext, // Recomposer
content: @Composable () -> Unit // 入口函数对象
): Composition {
...
// 创建 Composition
val original = Composition(UiApplier(owner.root), parent)
// 常见 WrappedComposition 并赋值到 AndroidComposeView 的Tag组数
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)
}
// 又来一个setContent
wrapped.setContent(content)
return wrapped
}
Composition(UiApplier(owner.root), parent)
: 之前分析了parent,就是通过工厂模式创建的Recomposer,那要理解Composition还需要看看UiApplier是一个什么类?
UiApplier
internal class UiApplier(
root: LayoutNode
)
参数需要一个LayoutNode,那这个LayoutNode又是什么了?
先从目前的流程场景分析的的话,就直接查看传递的AndroidComposeView.root
// androidx.compose.ui.platform.AndroidComposeView
override val root = LayoutNode().also {
it.measurePolicy = RootMeasurePolicy
it.modifier = Modifier
.then(semanticsModifier)
.then(_focusManager.modifier)
.then(keyInputModifier)
}
原来AndroidComposeView有这个属性对象,布局层次结构中的一个元素,用于compose UI构建,那又有一个问题了,当前是确定AndroidComposeView有这个元素,那入口函数对象也是用于compose UI构建,那它有没有了?
答案是肯定有的,查看一下Text()
最底层实现源码
// androidx.compose.foundation.text.CoreTextKt.class
internal fun CoreText(
...
) {
...
Layout(
content = if (inlineComposables.isEmpty()) {
{}
} else {
{ InlineChildren(text, inlineComposables) }
},
modifier = modifier
.then(controller.modifiers)
...
,
measurePolicy = controller.measurePolicy
)
...
}
// androidx.compose.ui.layout.LayoutKt.class
@Composable inline fun Layout(
content: @Composable () -> Unit,
modifier: Modifier = Modifier,
measurePolicy: MeasurePolicy
) {
val density = LocalDensity.current
val layoutDirection = LocalLayoutDirection.current
ReusableComposeNode>(
factory = ComposeUiNode.Constructor,
update = {
set(measurePolicy, ComposeUiNode.SetMeasurePolicy)
set(density, ComposeUiNode.SetDensity)
set(layoutDirection, ComposeUiNode.SetLayoutDirection)
},
skippableUpdate = materializerOf(modifier),
content = content
)
}
最终是调用了ReusableComposeNode
,而ComposeUiNode
就是LayoutNode的实现接口。
internal class LayoutNode : Measurable, Remeasurement, OwnerScope, LayoutInfo, ComposeUiNode
所以结合继承关系可以把LayoutNode结构理解成:
对于UiApplier,就可以小结一下
UiApplier是一个视图工具类,让其他使用者能更方便的操作root: LayoutNode
。内部维护了一个stack = mutableListOf
代表类似上图的视图树,当视图树插入、移除、移动等等都会更新stack。
那问题又来了,UiApplier是Composition的入参,究竟是哪个对象来操作UiApplier了?
Composition
// androidx.compose.runtime.CompositionKt.class
fun Composition(
applier: Applier<*>, // UiApplier
parent: CompositionContext // Recomposer
): Composition =
CompositionImpl(
parent,
applier
)
internal class CompositionImpl(
private val parent: CompositionContext, // Recomposer
private val applier: Applier<*>, // UiApplier
recomposeContext: CoroutineContext? = null
) : ControlledComposition {
...
// 保存所有视图组合的信息(核心数据结构)
private val slotTable = SlotTable()
...
private val composer: ComposerImpl =
ComposerImpl(
applier = applier,
parentContext = parent,
slotTable = slotTable,
abandonSet = abandonSet,
changes = changes,
composition = this
).also {
parent.registerComposer(it)
}
...
}
SlotTable
是一个很大的话题,类似于 Gap Buffer (间隙缓冲区) ,技术有限这边就不展开说了,官方可能觉得也很难理解,在知乎上说明了SlotTable
的执行模式。
所以可以说Composition是一个连接器,把视图树(UiApplier)和调度程序(Recomposer)连接到一起,当监听到数据(比如mutableState)变化或者添加视图等等,Recomposer会通过Composition通知SlotTable更新视图组合信息、UiApplier更新视图树等等,在分析UiApplier的Text()
底层实现源码中,最终是创建了ReusableComposeNode
,那在看看对应的源码。
@Composable inline fun > ReusableComposeNode(
noinline factory: () -> T,
update: @DisallowComposableCalls Updater.() -> Unit
) {
if (currentComposer.applier !is E) invalidApplier()
currentComposer.startReusableNode()
if (currentComposer.inserting) {
currentComposer.createNode { factory() }
} else {
currentComposer.useNode()
}
currentComposer.disableReusing()
Updater(currentComposer).update()
currentComposer.enableReusing()
currentComposer.endNode()
}
currentComposer
就是Composition中的composer
调用startReusableNode()
就是操作SlotReader,而SlotReader其实就是SlotTable的读取控制器,对应的SlotTable也有SlotWriter写入控制器。
调用createNode()
就是操作UiApplier
WrappedComposition.setContent()
终于讲到WrappedComposition.setContent()
,它是一个带有处理生命周期的包裹,把AndroidComposeView和Composition包裹,当AndroidComposeView被附加到ComposeView后,会添加LifecycleEventObserver
,之后触发生命周期Lifecycle.Event.ON_CREATE
,会先调用连接器Composition的setContent()
,再执行调度程序Recomposer的composeInitial()
,再调用连接器Composition的setContent() -> composeContent()
,再调用连接器中的带有SlotTable的composer.composeContent()
,最终执行invokeComposable()
来组合我们的入口函数对象。
调用链看起来很懵逼,关键是要理解Composition、composer、CompositionContext(Recomposer)、UiApplier每个对象的关系、职责和工作的传递,最后再看看相关涉及的源码。
// androidx.compose.ui.platform.WrappedComposition
private class WrappedComposition(
val owner: AndroidComposeView,
val original: Composition //
) : Composition, LifecycleEventObserver {
private var disposed = false
private var addedToLifecycle: Lifecycle? = null
private var lastContent: @Composable () -> Unit = {}
override fun setContent(content: @Composable () -> Unit) {
owner.setOnViewTreeOwnersAvailable {
if (!disposed) {
val lifecycle = it.lifecycleOwner.lifecycle
lastContent = content
if (addedToLifecycle == null) {
// 1. 初始化流程第一次会进入
addedToLifecycle = lifecycle
// 2. 设置生命周期监听
lifecycle.addObserver(this)
} else if (lifecycle.currentState.isAtLeast(Lifecycle.State.CREATED)) {
// 4. 调用连接器的setContent
original.setContent {
@Suppress("UNCHECKED_CAST")
val inspectionTable =
owner.getTag(R.id.inspection_slot_table_set) as?
MutableSet
?: (owner.parent as? View)?.getTag(R.id.inspection_slot_table_set)
as? MutableSet
if (inspectionTable != null) {
@OptIn(InternalComposeApi::class)
inspectionTable.add(currentComposer.compositionData)
currentComposer.collectParameterInformation()
}
LaunchedEffect(owner) { owner.keyboardVisibilityEventLoop() }
LaunchedEffect(owner) { owner.boundsUpdatesEventLoop() }
// 入口函数对象
CompositionLocalProvider(LocalInspectionTables provides inspectionTable) {
ProvideAndroidCompositionLocals(owner, content)
}
}
}
}
}
}
override fun dispose() {
if (!disposed) {
disposed = true
// 清空带生命周期的包裹
owner.view.setTag(R.id.wrapped_composition_tag, null)
// 移除生命周期舰艇
addedToLifecycle?.removeObserver(this)
}
original.dispose()
}
override val hasInvalidations get() = original.hasInvalidations
override val isDisposed: Boolean get() = original.isDisposed
override fun onStateChanged(source: LifecycleOwner, event: Lifecycle.Event) {
if (event == Lifecycle.Event.ON_DESTROY) {
dispose()
} else if (event == Lifecycle.Event.ON_CREATE) {
if (!disposed) {
// 3. 重新执行
setContent(lastContent)
}
}
}
}
Composition -> original.setContent()
// androidx.compose.runtime.CompositionImpl
override fun setContent(content: @Composable () -> Unit) {
check(!disposed) { "The composition is disposed" }
this.composable = content
// parent = Recomposer = ComposeView.compositionContext
parent.composeInitial(this, composable)
}
Recomposer -> parent.composeInitial(this, composable)
// androidx.compose.runtime.Recomposer
internal override fun composeInitial(
composition: ControlledComposition, // Recomposer
content: @Composable () -> Unit // 入口函数对象
) {
val composerWasComposing = composition.isComposing
composing(composition, null) {
// 走到连接器
composition.composeContent(content)
}
...
}
composition.composeContent(content)
// androidx.compose.runtime.CompositionImpl
override fun composeContent(content: @Composable () -> Unit) {
synchronized(lock) {
drainPendingModificationsForCompositionLocked()
// 走到内部composer
composer.composeContent(takeInvalidations(), content)
}
}
composer.composeContent(takeInvalidations(), content)
// androidx.compose.runtime.ComposerImpl
internal fun composeContent(
invalidationsRequested: IdentityArrayMap?>,
content: @Composable () -> Unit // 入口函数对象
) {
runtimeCheck(changes.isEmpty()) { "Expected applyChanges() to have been called" }
doCompose(invalidationsRequested, content)
}
private fun doCompose(
invalidationsRequested: IdentityArrayMap?>,
content: (@Composable () -> Unit)? // 入口函数对象
) {
runtimeCheck(!isComposing) { "Reentrant composition is not supported" }
trace("Compose:recompose") {
snapshot = currentSnapshot()
invalidationsRequested.forEach { scope, set ->
val location = scope.anchor?.location ?: return
invalidations.add(Invalidation(scope, location, set))
}
invalidations.sortBy { it.location }
nodeIndex = 0
var complete = false
isComposing = true
try {
startRoot()
// Ignore reads of derivedStatOf recalculations
observeDerivedStateRecalculations(
start = {
childrenComposing++
},
done = {
childrenComposing--
},
) {
if (content != null) {
startGroup(invocationKey, invocation)
// 真正处理入口函数对象的地方
invokeComposable(this, content)
endGroup()
} else {
skipCurrentGroup()
}
}
endRoot()
complete = true
} finally {
isComposing = false
invalidations.clear()
providerUpdates.clear()
if (!complete) abortRoot()
}
}
}
startRoot()、endGroup() 是操作SlotTable
最后再看看invokeComposable
做了什么
internal fun invokeComposable(composer: Composer, composable: @Composable () -> Unit) {
@Suppress("UNCHECKED_CAST")
val realFn = composable as Function2
realFn(composer, 1)
}
原来最终把我们的入口函数对象强转成Function2
,但是当要去查看Function2
是做什么的时候,没办法找到类。
原因是这边是compiler(基于kotlin的编译器插件)做的事情,它把我们的入口函数进行了修改,添加了一些参数,比如Int对应的就是groupId,所以才能强转成功。拿官方的例子来说明一下就能明白了。
@Composable
fun Counter() {
var count by remember { mutableStateOf(0) }
Button(
text="Count: $count",
onPress={ count += 1 }
)
}
fun Counter($composer: Composer, groupId: Int) {
$composer.start(groupId)
var count by remember { mutableStateOf(0) }
Button(
text="Count: $count",
onPress={ count += 1 }
)
$composer.end()
}
小结
初次看setContent源码,什么流程都记不住看不懂,只知道N个setContent跳来跳去,当把Composition、composer、CompositionContext(Recomposer)、UiApplier每个对象的关系、职责搞清楚之后,再对跳转流程理清楚,就能发现具体的工作流程了。
所分析的setContent()
只是Compose Ui体系中的一小部分,发现分析完后,延伸出了更多的知识点,比如SlotTable工作的流程和时间复杂度、LayoutNode和AndroidComposeView相关的测量绘制触摸反馈、mutableState刷新机制等等,还有分析过程中提出的一个问题Compose和传统View/ViewGroup互相交互并且能在同一个activity混合开发吗?
参考资料
官方 深入详解 Jetpack Compose | 实现原理