美文网首页
Jetpack Compose setContent 源码分析

Jetpack Compose setContent 源码分析

作者: 一迅 | 来源:发表于2021-09-15 10:48 被阅读0次

    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<ViewGroup>(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 结构图.png

    难道这个ComposeView肯定跟传统的View/ViewGroup有关系吗?遇事不决就看看Compose的继承关系。


    image.png

    果真如此,也能验证Compose架构的分层设计中上下关系,animationfoundation的工作也是在ComposeView中进行,另外还能延伸出一个问题,那是不是Compose和传统View/ViewGroup能够互相交互并且能在同一个activity混合开发?这个目前没有研究过,就先略过了。。。

    第二步的话,由于分析就是初始化状态的流程且existingComposeView肯定为空,所以直接进入到第三步,传递了parentsetParentCompositionContext方法,按照案例的入口函数,

    // 入口函数
    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<WindowRecomposerFactory>(
            // 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的结构图就可以理解为:

    AndroidComposeView 结构图.png
    // // 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<ComposeUiNode, Applier<Any>>(
            factory = ComposeUiNode.Constructor,
            update = {
                set(measurePolicy, ComposeUiNode.SetMeasurePolicy)
                set(density, ComposeUiNode.SetDensity)
                set(layoutDirection, ComposeUiNode.SetLayoutDirection)
            },
            skippableUpdate = materializerOf(modifier),
            content = content
        )
    }
    

    最终是调用了ReusableComposeNode<ComposeUiNode, Applier<Any>>,而ComposeUiNode就是LayoutNode的实现接口。

    internal class LayoutNode : Measurable, Remeasurement, OwnerScope, LayoutInfo, ComposeUiNode
    

    所以结合继承关系可以把LayoutNode结构理解成:

    LayoutNode 结构图.png

    对于UiApplier,就可以小结一下

    UiApplier是一个视图工具类,让其他使用者能更方便的操作root: LayoutNode。内部维护了一个stack = mutableListOf<T>()代表类似上图的视图树,当视图树插入、移除、移动等等都会更新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 <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()
        }
        currentComposer.disableReusing()
        Updater<T>(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<CompositionData>
                                    ?: (owner.parent as? View)?.getTag(R.id.inspection_slot_table_set)
                                        as? MutableSet<CompositionData>
                            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<RecomposeScopeImpl, IdentityArraySet<Any>?>,
        content: @Composable () -> Unit // 入口函数对象
    ) {
        runtimeCheck(changes.isEmpty()) { "Expected applyChanges() to have been called" }
        doCompose(invalidationsRequested, content)
    }
    
    private fun doCompose(
        invalidationsRequested: IdentityArrayMap<RecomposeScopeImpl, IdentityArraySet<Any>?>,
        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<Composer, Int, Unit>
        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跳来跳去,当把CompositioncomposerCompositionContext(Recomposer)UiApplier每个对象的关系、职责搞清楚之后,再对跳转流程理清楚,就能发现具体的工作流程了。

    所分析的setContent()只是Compose Ui体系中的一小部分,发现分析完后,延伸出了更多的知识点,比如SlotTable工作的流程和时间复杂度、LayoutNode和AndroidComposeView相关的测量绘制触摸反馈、mutableState刷新机制等等,还有分析过程中提出的一个问题Compose和传统View/ViewGroup互相交互并且能在同一个activity混合开发吗?

    参考资料

    官方 深入详解 Jetpack Compose | 实现原理

    相关文章

      网友评论

          本文标题:Jetpack Compose setContent 源码分析

          本文链接:https://www.haomeiwen.com/subject/yyekgltx.html