Jetpack Compose Compilier&Runtime浅析

关于Compose的一些事

Compose的结构

在这里插入图片描述Compose的通用结构如图所示,由代码、编译器插件、runtime库、以及各平台对应的UI库组成。首先需要明确的一点是,Compose的前几层结构,即不包括UI的其余部分,是一套控制树状节点的一套东西,其实是可以完全脱离UI层独立运行的。对于Android UI而言,这个节点就是LayoutNode类。

在这里插入图片描述

对于Android平台的compose,结构是这样的。除了Animation层之外,其他三层每一层都是对上层的封装,一般来说我们使用的都是Material层的,当然Foundation层和UI层也是直接使用的。另外,对于一些自定义要求比较高的自定义控件(指不能直接靠直接组合/封装现有控件的实现的控件)而言,不免要涉及到UI层的LayoutNode(Android Compose的节点类型)和Modifier、DrawScope等等麻烦的东西(这个我自己都没搞懂以后再说吧)。

举个例子,以最常用的Text方法为例。

最常用的Text位于Material层的Text,只是会生成一个mergedStyle

@Composable
fun Text(
    text: String,
    modifier: Modifier = Modifier,
    color: Color = Color.Unspecified,
    fontSize: TextUnit = TextUnit.Unspecified,
    fontStyle: FontStyle? = null,
    fontWeight: FontWeight? = null,
    fontFamily: FontFamily? = null,
    letterSpacing: TextUnit = TextUnit.Unspecified,
    textDecoration: TextDecoration? = null,
    textAlign: TextAlign? = null,
    lineHeight: TextUnit = TextUnit.Unspecified,
    overflow: TextOverflow = TextOverflow.Clip,
    softWrap: Boolean = true,
    maxLines: Int = Int.MAX_VALUE,
    onTextLayout: (TextLayoutResult) -> Unit = {},
    style: TextStyle = LocalTextStyle.current
) {
    Text(
        AnnotatedString(text),
        modifier,
        color,
        fontSize,
        fontStyle,
        fontWeight,
        fontFamily,
        letterSpacing,
        textDecoration,
        textAlign,
        lineHeight,
        overflow,
        softWrap,
        maxLines,
        emptyMap(),
        onTextLayout,
        style
    )
}

@Composable
fun Text(
    text: AnnotatedString,
    modifier: Modifier = Modifier,
    color: Color = Color.Unspecified,
    fontSize: TextUnit = TextUnit.Unspecified,
    fontStyle: FontStyle? = null,
    fontWeight: FontWeight? = null,
    fontFamily: FontFamily? = null,
    letterSpacing: TextUnit = TextUnit.Unspecified,
    textDecoration: TextDecoration? = null,
    textAlign: TextAlign? = null,
    lineHeight: TextUnit = TextUnit.Unspecified,
    overflow: TextOverflow = TextOverflow.Clip,
    softWrap: Boolean = true,
    maxLines: Int = Int.MAX_VALUE,
    inlineContent: Map<String, InlineTextContent> = mapOf(),
    onTextLayout: (TextLayoutResult) -> Unit = {},
    style: TextStyle = LocalTextStyle.current
) {
    val textColor = color.takeOrElse {
        style.color.takeOrElse {
            LocalContentColor.current.copy(alpha = LocalContentAlpha.current)
        }
    }
    val mergedStyle = style.merge(
        TextStyle(
            color = textColor,
            fontSize = fontSize,
            fontWeight = fontWeight,
            textAlign = textAlign,
            lineHeight = lineHeight,
            fontFamily = fontFamily,
            textDecoration = textDecoration,
            fontStyle = fontStyle,
            letterSpacing = letterSpacing
        )
    )
    BasicText(
        text,
        modifier,
        mergedStyle,
        onTextLayout,
        overflow,
        softWrap,
        maxLines,
        inlineContent
    )
}

然后就到了Foundation层的BasicText,就是直接调用CoreText

@Composable
fun BasicText(
    text: AnnotatedString,
    modifier: Modifier = Modifier,
    style: TextStyle = TextStyle.Default,
    onTextLayout: (TextLayoutResult) -> Unit = {},
    overflow: TextOverflow = TextOverflow.Clip,
    softWrap: Boolean = true,
    maxLines: Int = Int.MAX_VALUE,
    inlineContent: Map<String, InlineTextContent> = mapOf(),
) {
    CoreText(
        text,
        modifier.semantics { this.text = text },
        style,
        softWrap,
        overflow,
        maxLines,
        inlineContent,
        onTextLayout
    )
}

再到UI层的CoreText,有remember的state,有controller,还有生成最终LayoutNode的Layout方法

@Composable
@OptIn(InternalFoundationTextApi::class)
internal fun CoreText(
    text: AnnotatedString,
    modifier: Modifier = Modifier,
    style: TextStyle,
    softWrap: Boolean,
    overflow: TextOverflow,
    maxLines: Int,
    inlineContent: Map<String, InlineTextContent>,
    onTextLayout: (TextLayoutResult) -> Unit
) {
    require(maxLines > 0) { "maxLines should be greater than 0" }

    // selection registrar, if no SelectionContainer is added ambient value will be null
    val selectionRegistrar = LocalSelectionRegistrar.current
    val density = LocalDensity.current
    val resourceLoader = LocalFontLoader.current
    val selectionBackgroundColor = LocalTextSelectionColors.current.backgroundColor

    val (placeholders, inlineComposables) = resolveInlineContent(text, inlineContent)

    // The ID used to identify this CoreText. If this CoreText is removed from the composition
    // tree and then added back, this ID should stay the same.
    // Notice that we need to update selectable ID when the input text or selectionRegistrar has
    // been updated.
    // When text is updated, the selection on this CoreText becomes invalid. It can be treated
    // as a brand new CoreText.
    // When SelectionRegistrar is updated, CoreText have to request a new ID to avoid ID collision.
    val selectableId = rememberSaveable(text, selectionRegistrar) {
        selectionRegistrar?.nextSelectableId() ?: SelectionRegistrar.InvalidSelectableId
    }
    val state = remember {
        TextState(
            TextDelegate(
                text = text,
                style = style,
                density = density,
                softWrap = softWrap,
                resourceLoader = resourceLoader,
                overflow = overflow,
                maxLines = maxLines,
                placeholders = placeholders
            ),
            selectableId
        )
    }
    state.textDelegate = updateTextDelegate(
        current = state.textDelegate,
        text = text,
        style = style,
        density = density,
        softWrap = softWrap,
        resourceLoader = resourceLoader,
        overflow = overflow,
        maxLines = maxLines,
        placeholders = placeholders
    )
    state.onTextLayout = onTextLayout
    state.selectionBackgroundColor = selectionBackgroundColor

    val controller = remember { TextController(state) }
    controller.update(selectionRegistrar)

    Layout(
        content = if (inlineComposables.isEmpty()) {
            {}
        } else {
            { InlineChildren(text, inlineComposables) }
        },
        modifier = modifier
            .then(controller.modifiers)
            .then(
                if (selectionRegistrar != null) {
                    if (isInTouchMode) {
                        Modifier.pointerInput(controller.longPressDragObserver) {
                            detectDragGesturesAfterLongPressWithObserver(
                                controller.longPressDragObserver
                            )
                        }
                    } else {
                        Modifier.pointerInput(controller.mouseSelectionObserver) {
                            mouseSelectionDetector(
                                controller.mouseSelectionObserver,
                                finalPass = true
                            )
                        }
                    }
                } else {
                    Modifier
                }
            ),
        measurePolicy = controller.measurePolicy
    )

    DisposableEffect(selectionRegistrar, effect = controller.commit)
}

Compose Compiler

Compose Compiler层其实是kotlin的Compiler plugin

composeOptions {
        kotlinCompilerExtensionVersion compose_version
        kotlinCompilerVersion '1.5.10'
    }

虽然Compose在我们一般使用中的表现似乎就只是一个@Composable的注解,但是处理这个注解的并不是一个apt,而是一个Compiler plugin。这个@Composable注解某种意义上不能算是一个普通的注解,主要原因还是因为Compiler plugin和apt能做的事情差太多了。

Kotlin Compiler Plugin和apt的区别

  1. apt运行在实际编译之前,插件是在实际运行之中调用
  2. 插件的速度比apt快很多
  3. 插件可以处理/获取的信息更多,包括静态的代码检测(idea输入中的时候的检测),输出java/IR,支持多平台
  4. 插件没有文档,并且需要适配kotlin版本(这也是为什么compose需要特定的kotlin版本的原因),apt有文档
  5. 插件太难了

这个太难了我也没有深入学习,参考资料在此

另外谷歌官方还有一个推荐用来替代apt的东西,Kotlin Symbol Processing (KSP) KSP是用来开发轻量级插件,速度快,也有文档,不用关心kotlin版本变化,虽然功能上比完整Compiler插件少一点但是也很强大,有兴趣可以看看。

Compose Compiler结构

在这里插入图片描述

上张图,有兴趣的自己看吧

Compose的注解(们)

@Composable

首先从概念上来说,@Composable的方法的意义是发射一个节点,不管是UI节点或者是别的数据节点,这个从下面的反编译的结果也是可以看到的。

@ExperimentalComposeApi/@ComposeCompilerApi/@InternalComposeApi

跟平常使用没什么关系

@DisallowComposableCalls

禁止标注的方法内部调用@Composable方法,用于防止inline方法里的lambda方法参数发射节点。举个例子最常用的remember方法

@Composable
inline fun <T> remember(calculation: @DisallowComposableCalls () -> T): T =
    currentComposer.cache(false, calculation)
@ReadOnlyComposable

不生成节点只读的@Composable,举个例子

@Composable
@ReadOnlyComposable
fun stringResource(@StringRes id: Int): String {
    val resources = resources()
    return resources.getString(id)
}

编译后

public static final String stringResource(int id, Composer $composer, int $changed) {
        ComposerKt.sourceInformationMarkerStart($composer, 383449052, "C(stringResource)35@1191L11:StringResources.android.kt#ccshc7");
        String string = resources($composer, 0).getString(id);
        Intrinsics.checkNotNullExpressionValue(string, "resources.getString(id)");
        ComposerKt.sourceInformationMarkerEnd($composer);
        return string;
    }
@NonRestartableComposable

标记的方法生成的结果不可重启/跳过

@Composable
@NonRestartableComposable
fun NonRestartableTestWithSth(text: String) {
    Text(text = text)
}
public static final void NonRestartableTestWithSth(String text, Composer $composer, int $changed) {
        Intrinsics.checkNotNullParameter(text, "text");
        $composer.startReplaceableGroup(1990981932);
        ComposerKt.sourceInformation($composer, "C(NonRestartableTestWithSth)66@1046L17:Test2.kt#ptgicz");
        TextKt.m1011TextfLXpl1I(text, null, 0, 0, null, null, null, 0, null, null, 0, 0, false, 0, null, null, $composer, $changed & 14, 64, 65534);
        $composer.endReplaceableGroup();
    }

可以看到变成了ReplaceableGroup,并且changed也没有用到

@Immutable/@Stable

定义略有不同,但都是标记纯函数或者不可变成员等等帮助Compiler优化recompose过程

Compose Compiler对代码做的事

@Composable
fun EmptyTest() {

}

@Composable
fun notEmptyTest(): Int {
    return 1
}

@Composable
fun ComposableLambdaTest(t: @Composable () -> Unit) {
    t()
}

@Composable
fun ComposableLambdaWithParamTest(t: @Composable (i:Int) -> Unit) {
    t(1)
}

@Composable
fun LambdaTest(t: () -> Unit) {
    t()
}

@Composable
fun ComposableLambdaReturnTest(t: @Composable () -> Int) {
    t()
}

@Composable
fun TextTest(text:String){
    Text(text = text)
}

@Composable
fun RememberTest() {
    var i by remember {
        mutableStateOf(1)
    }
    Button(onClick = { i++ }) {
        Text(text = "test")
    }
}
 public static final void EmptyTest(Composer $composer, int $changed) {
        Composer $composer2 = $composer.startRestartGroup(-532712360);
        ComposerKt.sourceInformation($composer2, "C(EmptyTest):Test2.kt#ptgicz");
        if ($changed == 0 && $composer2.getSkipping()) {
            $composer2.skipToGroupEnd();
        }
        ScopeUpdateScope endRestartGroup = $composer2.endRestartGroup();
        if (endRestartGroup != null) {
            endRestartGroup.updateScope(new Test2Kt$EmptyTest$1($changed));
        }
    }

    public static final int notEmptyTest(Composer $composer, int $changed) {
        $composer.startReplaceableGroup(1071305719);
        ComposerKt.sourceInformation($composer, "C(notEmptyTest):Test2.kt#ptgicz");
        int r0 = LiveLiterals$Test2Kt.INSTANCE.m3723Int$funnotEmptyTest();
        $composer.endReplaceableGroup();
        return r0;
    }

    public static final void ComposableLambdaTest(Function2<? super Composer, ? super Integer, Unit> function2, Composer $composer, int $changed) {
        Intrinsics.checkNotNullParameter(function2, "t");
        Composer $composer2 = $composer.startRestartGroup(-209824173);
        ComposerKt.sourceInformation($composer2, "C(ComposableLambdaTest)24@398L3:Test2.kt#ptgicz");
        int $dirty = $changed;
        if (($changed & 14) == 0) {
            $dirty |= $composer2.changed(function2) ? 4 : 2;
        }
        if ((($dirty & 11) ^ 2) != 0 || !$composer2.getSkipping()) {
            function2.invoke($composer2, Integer.valueOf($dirty & 14));
        } else {
            $composer2.skipToGroupEnd();
        }
        ScopeUpdateScope endRestartGroup = $composer2.endRestartGroup();
        if (endRestartGroup != null) {
            endRestartGroup.updateScope(new Test2Kt$ComposableLambdaTest$1(function2, $changed));
        }
    }

public static final void ComposableLambdaWithParamTest(Function3<? super Integer, ? super Composer, ? super Integer, Unit> function3, Composer $composer, int $changed) {
        Intrinsics.checkNotNullParameter(function3, "t");
        Composer $composer2 = $composer.startRestartGroup(-2040920042);
        ComposerKt.sourceInformation($composer2, "C(ComposableLambdaWithParamTest)30@520L4:Test2.kt#ptgicz");
        int $dirty = $changed;
        if (($changed & 14) == 0) {
            $dirty |= $composer2.changed(function3) ? 4 : 2;
        }
        if ((($dirty & 11) ^ 2) != 0 || !$composer2.getSkipping()) {
            function3.invoke(Integer.valueOf(LiveLiterals$Test2Kt.INSTANCE.m3723Int$arg0$callinvoke$funComposableLambdaWithParamTest()), $composer2, Integer.valueOf(($dirty << 3) & 112));
        } else {
            $composer2.skipToGroupEnd();
        }
        ScopeUpdateScope endRestartGroup = $composer2.endRestartGroup();
        if (endRestartGroup != null) {
            endRestartGroup.updateScope(new Test2Kt$ComposableLambdaWithParamTest$1(function3, $changed));
        }
    }

    public static final void LambdaTest(Function0<Unit> function0, Composer $composer, int $changed) {
        Intrinsics.checkNotNullParameter(function0, "t");
        Composer $composer2 = $composer.startRestartGroup(2050120749);
        ComposerKt.sourceInformation($composer2, "C(LambdaTest):Test2.kt#ptgicz");
        int $dirty = $changed;
        if (($changed & 14) == 0) {
            $dirty |= $composer2.changed(function0) ? 4 : 2;
        }
        if ((($dirty & 11) ^ 2) != 0 || !$composer2.getSkipping()) {
            function0.invoke();
        } else {
            $composer2.skipToGroupEnd();
        }
        ScopeUpdateScope endRestartGroup = $composer2.endRestartGroup();
        if (endRestartGroup != null) {
            endRestartGroup.updateScope(new Test2Kt$LambdaTest$1(function0, $changed));
        }
    }

    public static final void ComposableLambdaReturnTest(Function2<? super Composer, ? super Integer, Integer> function2, Composer $composer, int $changed) {
        Intrinsics.checkNotNullParameter(function2, "t");
        Composer $composer2 = $composer.startRestartGroup(992700103);
        ComposerKt.sourceInformation($composer2, "C(ComposableLambdaReturnTest)34@535L3:Test2.kt#ptgicz");
        int $dirty = $changed;
        if (($changed & 14) == 0) {
            $dirty |= $composer2.changed(function2) ? 4 : 2;
        }
        if ((($dirty & 11) ^ 2) != 0 || !$composer2.getSkipping()) {
            function2.invoke($composer2, Integer.valueOf($dirty & 14));
        } else {
            $composer2.skipToGroupEnd();
        }
        ScopeUpdateScope endRestartGroup = $composer2.endRestartGroup();
        if (endRestartGroup != null) {
            endRestartGroup.updateScope(new Test2Kt$ComposableLambdaReturnTest$1(function2, $changed));
        }
    }

    public static final void TextTest(String text, Composer $composer, int $changed) {
        Composer $composer2;
        Intrinsics.checkNotNullParameter(text, "text");
        Composer $composer3 = $composer.startRestartGroup(-2000670640);
        ComposerKt.sourceInformation($composer3, "C(TextTest)39@585L17:Test2.kt#ptgicz");
        int $dirty = $changed;
        if (($changed & 14) == 0) {
            $dirty |= $composer3.changed(text) ? 4 : 2;
        }
        if ((($dirty & 11) ^ 2) != 0 || !$composer3.getSkipping()) {
            $composer2 = $composer3;
            TextKt.m1011TextfLXpl1I(text, null, 0, 0, null, null, null, 0, null, null, 0, 0, false, 0, null, null, $composer2, $dirty & 14, 64, 65534);
        } else {
            $composer3.skipToGroupEnd();
            $composer2 = $composer3;
        }
        ScopeUpdateScope endRestartGroup = $composer2.endRestartGroup();
        if (endRestartGroup != null) {
            endRestartGroup.updateScope(new Test2Kt$TextTest$1(text, $changed));
        }
    }

/* JADX INFO: Multiple debug info for r8v5 'value$iv$iv'  java.lang.Object: [D('$i$a$-remember-Test2Kt$RememberTest$1' int), D('value$iv$iv' java.lang.Object)] */
    public static final void RememberTest(Composer $composer, int $changed) {
        Object value$iv$iv;
        Object value$iv$iv2;
        Composer $composer2 = $composer.startRestartGroup(2143490257);
        ComposerKt.sourceInformation($composer2, "C(RememberTest)45@685L42,48@749L7,48@732L61:Test2.kt#ptgicz");
        if ($changed != 0 || !$composer2.getSkipping()) {
            $composer2.startReplaceableGroup(-3687241);
            ComposerKt.sourceInformation($composer2, "C(remember):Composables.kt#9igjgp");
            Object it$iv$iv = $composer2.rememberedValue();
            if (it$iv$iv == Composer.Companion.getEmpty()) {
                value$iv$iv = SnapshotStateKt.mutableStateOf$default(Integer.valueOf(LiveLiterals$Test2Kt.INSTANCE.m3723xbaeee748()), null, 2, null);
                $composer2.updateRememberedValue(value$iv$iv);
            } else {
                value$iv$iv = it$iv$iv;
            }
            $composer2.endReplaceableGroup();
            MutableState i$delegate = (MutableState) value$iv$iv;
            $composer2.startReplaceableGroup(-3686930);
            ComposerKt.sourceInformation($composer2, "C(remember)P(1):Composables.kt#9igjgp");
            boolean invalid$iv$iv = $composer2.changed(i$delegate);
            Object it$iv$iv2 = $composer2.rememberedValue();
            if (invalid$iv$iv || it$iv$iv2 == Composer.Companion.getEmpty()) {
                value$iv$iv2 = (Function0) new Test2Kt$RememberTest$1$1(i$delegate);
                $composer2.updateRememberedValue(value$iv$iv2);
            } else {
                value$iv$iv2 = it$iv$iv2;
            }
            $composer2.endReplaceableGroup();
            ButtonKt.Button((Function0) value$iv$iv2, null, false, null, null, null, null, null, null, ComposableSingletons$Test2Kt.INSTANCE.m3688getLambda1$app_debug(), $composer2, 0, 510);
        } else {
            $composer2.skipToGroupEnd();
        }
        ScopeUpdateScope endRestartGroup = $composer2.endRestartGroup();
        if (endRestartGroup != null) {
            endRestartGroup.updateScope(new Test2Kt$RememberTest$2($changed));
        }
    }

    /* access modifiers changed from: private */
    /* renamed from: RememberTest$lambda-1  reason: not valid java name */
    public static final int m3798RememberTest$lambda1(MutableState<Integer> $this$getValue$iv) {
        return $this$getValue$iv.getValue().intValue();
    }

    /* access modifiers changed from: private */
    /* renamed from: RememberTest$lambda-2  reason: not valid java name */
    public static final void m3799RememberTest$lambda2(MutableState<Integer> mutableState, int value) {
        mutableState.setValue(Integer.valueOf(value));
    }

根据这几个常用的例子,也可以看到Compiler对于代码的修改。首先可以看到,对于一个@Composable方法,就算是空的,也会注入一个composer和一个changed,composer是直接操作生成节点的类,changed是用来智能跳过未改变部分的一个参数。对于@Composable的lambda方法会生成一个固定格式的方法,前面是入参,中间是compoesr和changed,最后是返回值。Compiler层对于代码的表现就这些,要继续理解整个流程,下面继续分析Runtime层。

Compose Runtime

Compose Runtime都有些什么

在这里插入图片描述
按顺序介绍一下比较重要的几个部分:

  1. Applier接口:操作生成的Node,是使得生成的节点产生管理的地方

  2. @Composable:标注产生group的方法

  3. Composer接口:直接操作SlotTable和Applier的类,唯一实现ComposerImpl

  4. Composition接口:有一个继承的接口ControlledComposition,ControlledComposition有一个Runtime层唯一实现CompositionImpl,用于连接CompositionContext和Composer,持有一个composable,是setContent和applyChanges发生的地方

  5. CompositionLocal:Composition的本地变量,注册之后用provide提供,用current拿,会在编译的时候生成代码

  6. Effects:安全保存side-effect的一些方法。side-effect指的是一些可能会修改全局变量或者读写IO的方法产生的副作用,@Composable方法应该都是无副作用的,因为@Composable方法会运行很多次,而且也不好准确预测一定会运行几次,所以这种side-effect需要用effects里的方法操作一下。

  7. MutableState&Snapshot:常用的MutableState是一个继承SnapshotMutableState,SnapshotMutableState和Snapshot是结合使用的,state是用来向Snapshot读写一个值,Snapshot有两个作用,一个是可以订阅读写的observer,另一个是版本隔离,可以切一个分支Snapshot出来然后在分支Shapshot上修改再apply到主的Snapshot,并且可以定义冲突的policy,这个也支持recompose可以并发的运行。

  8. PausableMonotonicFrameClock:可以pause和resume的FrameClock,animation都是依靠这个FrameClock,对于Android而言,就是包了一层AndroidUiFrameClock,最后还是调用的Choreographer.FrameCallback

  9. CompositionContext&Recomposer&CompositionContextImpl:Recomposer和CompositionContextImpl是两种实现。Recomposer是一颗树对应的唯一一个控制整体recompose流程的东西,存了所有的compositions。CompositionContextImpl是ComposerImpl的内部类,存了一些composers,主要是SubCompose用(LazyList之类的)。

  10. SlotTable:一个基于GapBuffer的存储结构,groups里存了id等信息,slots里对应groups的数据,一个group可能存了多个slot。每五位groups表示了一个group的信息

    // Group layout
    //  0             | 1             | 2             | 3             | 4             |
    //  Key           | Group info    | Parent anchor | Size          | Data anchor   |
    

在这里插入图片描述

SlotTable(GapBuffer)的插入过程

以下插入过程是抽象的过程,不是实际的SlotTable插入过程(因为我看不懂)

当GapBuffer为空的时候是这样的在这里插入图片描述

在插入一些数据之后
在这里插入图片描述

然后当我们需要更新slot1的时候在这里插入图片描述

首先先把Gap移到需要插入的位置
在这里插入图片描述

然后再插入
在这里插入图片描述

移除的过程更简单,先把Gap移到需要移除的位置后面,然后直接把Gap扩大就行了

使用GapBuffer的好处在于,由于UI大部分都是整块更新的,所以可以牺牲移动Gap的性能O(n),让更新UI做到O(1)

举个例子,一个简单的counter:

@Composable
fun Counter() {
 var count by remember { mutableStateOf(0) }
 Button(
   text = "Count: $count",
   onClick = { count += 1 }
 )
}

注入composer之后差不多是这样:

fun Counter($composer: Composer) {
 $composer.start(123)
 var count by remember($composer) { mutableStateOf(0) }
 Button(
   $composer,
   text = "Count: $count",
   onClick = { count += 1 },
 )
 $composer.end()
}

它存储起来的时候应该是这样:

在这里插入图片描述

Recompose的插入删除的过程也是一样的,顺序的对比已有的group的id和新的id决定要不要替换等等,实际的过程比上面的复杂很多。

Android UI的setContent过程(初始化过程)

public fun ComponentActivity.setContent(
    parent: CompositionContext? = null,
    content: @Composable () -> Unit
) {
  ...
  ComposeView(this).apply {
    ...
        setContent(content)
    ...
        setContentView(this, DefaultActivityContentLayoutParams)
    }
}

从上面可以看到我们最底层的view是一个ComposeView

ComposeView
fun setContent(content: @Composable () -> Unit) {
       ...
       createComposition() 
       ...
    }

fun createComposition() {
        ...
        ensureCompositionCreated()
    }

private fun ensureCompositionCreated() {
        ...
        composition = setContent(resolveParentCompositionContext()) {
              Content()
        ...
    }

private fun resolveParentCompositionContext() = parentContext
        ?: findViewTreeCompositionContext()?.also { cachedViewTreeCompositionContext = it }
        ?: cachedViewTreeCompositionContext
  //这里创建windowRecomposer
        ?: windowRecomposer.also { cachedViewTreeCompositionContext = it }
  
private fun View.createLifecycleAwareViewTreeRecomposer(): Recomposer {
    val currentThreadContext = AndroidUiDispatcher.CurrentThread
    val pausableClock = currentThreadContext[MonotonicFrameClock]?.let {
        PausableMonotonicFrameClock(it).apply { pause() }
    }
    val contextWithClock = currentThreadContext + (pausableClock ?: EmptyCoroutineContext)
    val recomposer = Recomposer(contextWithClock)
    val runRecomposeScope = CoroutineScope(contextWithClock)
    val viewTreeLifecycleOwner = checkNotNull(ViewTreeLifecycleOwner.get(this)) {
        "ViewTreeLifecycleOwner not found from $this"
    }
    // Removing the view holding the ViewTreeRecomposer means we may never be reattached again.
    // Since this factory function is used to create a new recomposer for each invocation and
    // doesn't reuse a single instance like other factories might, shut it down whenever it
    // becomes detached. This can easily happen as part of setting a new content view.
    addOnAttachStateChangeListener(
        object : View.OnAttachStateChangeListener {
            override fun onViewAttachedToWindow(v: View?) {}
            override fun onViewDetachedFromWindow(v: View?) {
                removeOnAttachStateChangeListener(this)
                recomposer.cancel()
            }
        }
    )
    viewTreeLifecycleOwner.lifecycle.addObserver(
        object : LifecycleEventObserver {
            override fun onStateChanged(lifecycleOwner: LifecycleOwner, event: Lifecycle.Event) {
                val self = this
                @Suppress("NON_EXHAUSTIVE_WHEN")
                when (event) {
                    Lifecycle.Event.ON_CREATE ->
                        // Undispatched launch since we've configured this scope
                        // to be on the UI thread
                        runRecomposeScope.launch(start = CoroutineStart.UNDISPATCHED) {
                            try {
                                recomposer.runRecomposeAndApplyChanges()
                            } finally {
                                // If runRecomposeAndApplyChanges returns or this coroutine is
                                // cancelled it means we no longer care about this lifecycle.
                                // Clean up the dangling references tied to this observer.
                                lifecycleOwner.lifecycle.removeObserver(self)
                            }
                        }
                    Lifecycle.Event.ON_START -> pausableClock?.resume()
                    Lifecycle.Event.ON_STOP -> pausableClock?.pause()
                    Lifecycle.Event.ON_DESTROY -> {
                        recomposer.cancel()
                    }
                }
            }
        }
    )
    return recomposer
}
  
  internal fun ViewGroup.setContent(
    parent: CompositionContext,
    content: @Composable () -> Unit
): Composition {
    //这里先保证GlobalSnapshot监听的启动
    //然后ComposeView再add了一个AndroidComposeView的child
    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)
}

  @OptIn(InternalComposeApi::class)
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()
    }
  //接下来是生成一个Composition,包装一下绑给了AndroidComposeView
    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
}
  
  
  //然后到了WrappedComposition
 @OptIn(InternalComposeApi::class)
    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)) {
                  //调用Composition.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() }

                      //设置一堆Android的CompositionLocal
                        CompositionLocalProvider(LocalInspectionTables provides inspectionTable) {
                            ProvideAndroidCompositionLocals(owner, content)
                        }
                    }
                }
            }
        }
    }
  
  //CompositionImpl
override fun setContent(content: @Composable () -> Unit) {
        check(!disposed) { "The composition is disposed" }
        this.composable = content
  //对于最底层的Composition,parent就是Recomposer,对于subComposition,parent是ComposeContextImpl,但最终调用还是会到Recomposer.composeInitial
        parent.composeInitial(this, composable)
    }
  
//Recomposer
 internal override fun composeInitial(
        composition: ControlledComposition,
        content: @Composable () -> Unit
    ) {
        val composerWasComposing = composition.isComposing
        composing(composition, null) {
            composition.composeContent(content)
        }
        // TODO(b/143755743)
        if (!composerWasComposing) {
            Snapshot.notifyObjectsInitialized()
        }
        //最终是这里执行了操作SlotTable
        composition.applyChanges()

        synchronized(stateLock) {
            if (_state.value > State.ShuttingDown) {
                if (composition !in knownCompositions) {
                    knownCompositions += composition
                }
            }
        }

        if (!composerWasComposing) {
            // Ensure that any state objects created during applyChanges are seen as changed
            // if modified after this call.
            Snapshot.notifyObjectsInitialized()
        }
    }
  
  private inline fun <T> composing(
        composition: ControlledComposition,
        modifiedValues: IdentityArraySet<Any>?,
        block: () -> T
    ): T {
    //在snapshot里执行composition.composeContent
        val snapshot = Snapshot.takeMutableSnapshot(
            readObserverOf(composition), writeObserverOf(composition, modifiedValues)
        )
        try {
            return snapshot.enter(block)
        } finally {
            applyAndCheck(snapshot)
        }
    }

//CompositionImpl
override fun composeContent(content: @Composable () -> Unit) {
        // TODO: This should raise a signal to any currently running recompose calls
        // to halt and return
        synchronized(lock) {
            drainPendingModificationsForCompositionLocked()
            composer.composeContent(takeInvalidations(), content)
        }
    }
  
//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)
                      //调用一直包着的content方法,也就是我们SetContent里的方法
                        invokeComposable(this, content)
                        endGroup()
                    } else {
                        skipCurrentGroup()
                    }
                }
              //结束根节点
                endRoot()
                complete = true
            } finally {
                isComposing = false
                invalidations.clear()
                providerUpdates.clear()
                if (!complete) abortRoot()
            }
        }
    }

然后在我们的@Composable方法中,最终都会调用到以下两个方法

@Suppress("NONREADONLY_CALL_IN_READONLY_COMPOSABLE", "UnnecessaryLambdaCreation")
@Composable inline fun <T : Any, reified E : Applier<*>> ComposeNode(
    noinline factory: () -> T,
    update: @DisallowComposableCalls Updater<T>.() -> Unit
) {
    if (currentComposer.applier !is E) invalidApplier()
    currentComposer.startNode()
    if (currentComposer.inserting) {
        currentComposer.createNode { factory() }
    } else {
        currentComposer.useNode()
    }
    Updater<T>(currentComposer).update()
    currentComposer.endNode()
}

@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()
    }
    currentComposer.disableReusing()
    Updater<T>(currentComposer).update()
    currentComposer.enableReusing()
    currentComposer.endNode()
}

可以看到都调用了Composer.createNode

@Suppress("UNUSED")
    override fun <T> createNode(factory: () -> T) {
        validateNodeExpected()
        runtimeCheck(inserting) { "createNode() can only be called when inserting" }
        val insertIndex = nodeIndexStack.peek()
        val groupAnchor = writer.anchor(writer.parent)
        groupNodeCount++
      //记录到insertFixups列表
        recordFixup { applier, slots, _ ->
            @Suppress("UNCHECKED_CAST")
            val node = factory()
            //更新slotTable
            slots.updateNode(groupAnchor, node)
            @Suppress("UNCHECKED_CAST") val nodeApplier = applier as Applier<T>
            //用Applier更新节点关系
            nodeApplier.insertTopDown(insertIndex, node)
            applier.down(node)
        }
        recordInsertUpFixup { applier, slots, _ ->
            @Suppress("UNCHECKED_CAST")
            val nodeToInsert = slots.node(groupAnchor)
            applier.up()
            @Suppress("UNCHECKED_CAST") val nodeApplier = applier as Applier<Any?>
            nodeApplier.insertBottomUp(insertIndex, nodeToInsert)
        }
    }

insertFixups列表是在end一个节点的时候如果需要记录插入再使用

 private fun recordInsert(anchor: Anchor) {
        if (insertFixups.isEmpty()) {
            val insertTable = insertTable
            recordSlotEditingOperation { _, slots, _ ->
                slots.beginInsert()
                slots.moveFrom(insertTable, anchor.toIndexFor(insertTable))
                slots.endInsert()
            }
        } else {
            val fixups = insertFixups.toMutableList()
            insertFixups.clear()
            realizeUps()
            realizeDowns()
            val insertTable = insertTable
          //这里把整个方法包起来丢到了changes列表里,最后在Composition.applyChanges里用
          //这个方法是先把fixups写到insertTable,然后再从insertTabel复制到slotTable
            recordSlotEditingOperation { applier, slots, rememberManager ->
                insertTable.write { writer ->
                    fixups.fastForEach { fixup ->
                        fixup(applier, writer, rememberManager)
                    }
                }
                slots.beginInsert()
                slots.moveFrom(insertTable, anchor.toIndexFor(insertTable))
                slots.endInsert()
            }
        }
    }

//最终写入的地方
 override fun applyChanges() {
        synchronized(lock) {
            val manager = RememberEventDispatcher(abandonSet)
            try {
                applier.onBeginChanges()

                // Apply all changes
                slotTable.write { slots ->
                    val applier = applier
                    changes.fastForEach { change ->
                        change(applier, slots, manager)
                    }
                    changes.clear()
                }

                applier.onEndChanges()

                // Side effects run after lifecycle observers so that any remembered objects
                // that implement RememberObserver receive onRemembered before a side effect
                // that captured it and operates on it can run.
                manager.dispatchRememberObservers()
                manager.dispatchSideEffects()

                if (pendingInvalidScopes) {
                    pendingInvalidScopes = false
                    observations.removeValueIf { scope -> !scope.valid }
                    derivedStates.removeValueIf { derivedValue -> derivedValue !in observations }
                }
            } finally {
                manager.dispatchAbandons()
            }
            drainPendingModificationsLocked()
        }
    }

最后看一眼对于Android UI,UIApplier是如何插入LayoutNode的

internal class UiApplier(
    root: LayoutNode
) : AbstractApplier<LayoutNode>(root) {
    ...

    override fun insertBottomUp(index: Int, instance: LayoutNode) {
      //current就是一个LayoutNode
        current.insertAt(index, instance)
    }

    ...
}

//LayoutNode
internal fun insertAt(index: Int, instance: LayoutNode) {
        check(instance._foldedParent == null) {
            "Cannot insert $instance because it already has a parent." +
                " This tree: " + debugTreeToString() +
                " Other tree: " + instance._foldedParent?.debugTreeToString()
        }
        check(instance.owner == null) {
            "Cannot insert $instance because it already has an owner." +
                " This tree: " + debugTreeToString() +
                " Other tree: " + instance.debugTreeToString()
        }

        if (DebugChanges) {
            println("$instance added to $this at index $index")
        }
  
        //将child和this建立parent和child的联系
        instance._foldedParent = this
        _foldedChildren.add(index, instance)
        onZSortedChildrenInvalidated()

        if (instance.isVirtual) {
            require(!isVirtual) { "Virtual LayoutNode can't be added into a virtual parent" }
            virtualChildrenCount++
        }
        invalidateUnfoldedVirtualChildren()

        instance.outerLayoutNodeWrapper.wrappedBy = innerLayoutNodeWrapper
      
        //将child和owner(AndroidComposeView)建立联系
        val owner = this.owner
        if (owner != null) {
            instance.attach(owner)
        }
    }

这个LayoutNode是怎么measure,layout,draw的部分就和Runtime没关系了,以后再说

Recompose的过程

首先明确一下我们这里分析的recompose流程是分析因为mustableState发生变化引起的recompose(别的情况暂时我也不知道)

fun <T> mutableStateOf(
    value: T,
    policy: SnapshotMutationPolicy<T> = structuralEqualityPolicy()
): MutableState<T> = createSnapshotMutableState(value, policy)

//最终会创建一个SnapshotMutableStateImpl
internal open class SnapshotMutableStateImpl<T>(
    value: T,
    override val policy: SnapshotMutationPolicy<T>
) : StateObject, SnapshotMutableState<T> {
    @Suppress("UNCHECKED_CAST")
    override var value: T
        get() = next.readable(this).value
        //调用StateStateRecord.overwritable
        set(value) = next.withCurrent {
            if (!policy.equivalent(it.value, value)) {
                next.overwritable(this, it) { this.value = value }
            }
        }
  
    private var next: StateStateRecord<T> = StateStateRecord(value)
}

//StateStateRecord
internal inline fun <T : StateRecord, R> T.overwritable(
    state: StateObject,
    candidate: T,
    block: T.() -> R
): R {
    var snapshot: Snapshot = snapshotInitializer
    return sync {
      //currentSnapshot可能是ThreadLocal里的一个MustableSnapshot(在@Composable方法中都是这样,因为在Recomposer.composing方法中会先takeMutableSnapshot),或者是一个全局的GlobalSnapshot(也是一个MutableSnapshot)
        snapshot = Snapshot.current
      //写入当前snapshot,最后是存到snapshot的modifiedSet里
        this.overwritableRecord(state, snapshot, candidate).block()
    }.also {
      //通知snapshot的writeObserver
        notifyWrite(snapshot, state)
    }
}

根据上面的snapshot的类型区别(MutableSnapshot或者GlobalSnapshot)会有两个路径。

MutableSnapshot就是在@Composable中调用时候的Snapshot,GlobalSnapshot就是在@Composable外部(比如一个Button的onClick的方法里)

GlobalSnapshot中

对于GlobalSnapshot,上面提到过,在ViewGroup.setContent方法里,会调用GlobalSnapshotManager.ensureStarted()

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)
            }
        }
    }
}

这个很简单,注册一个writeObserver,接收到事件就Snapshot.sendApplyNotifications()

 fun sendApplyNotifications() {
            val changes = sync {
                currentGlobalSnapshot.get().modified?.isNotEmpty() == true
            }
            if (changes)
                advanceGlobalSnapshot()
        }
        
private fun <T> advanceGlobalSnapshot(block: (invalid: SnapshotIdSet) -> T): T {
    val previousGlobalSnapshot = currentGlobalSnapshot.get()
    val result = sync {
        takeNewGlobalSnapshot(previousGlobalSnapshot, block)
    }

    // If the previous global snapshot had any modified states then notify the registered apply
    // observers.
    //已改变的set
    val modified = previousGlobalSnapshot.modified
    if (modified != null) {
    //applyObservers是一个全局变量
        val observers: List<(Set<Any>, Snapshot) -> Unit> = sync { applyObservers.toMutableList() }
        observers.fastForEach { observer ->
            observer(modified, previousGlobalSnapshot)
        }
    }

    return result
}

applyObservers有两个注册的地方,一个是SnapshotStateObserver(暂时不知道干嘛的,目测是给UI层用的),另一个是Recomposer的recompositionRunner,会存到Recomposer的snapshotInvalidations列表里

讲完modified数据丢到哪之后就必须要讲一下,Recomposer是怎么监听变化然后自动recompose的

首先入口方法是在Recomposer.runRecomposeAndApplyChanges(或者是runRecomposeConcurrentlyAndApplyChanges,当然这个并发方法Android没用到)的方法,对于Android UI而言是在View.createLifecycleAwareViewTreeRecomposer()调用的时候注册了LifecycleEventObserver,onCreate的时候自动调用的

 suspend fun runRecomposeAndApplyChanges() = recompositionRunner { parentFrameClock ->
        val toRecompose = mutableListOf<ControlledComposition>()
        val toApply = mutableListOf<ControlledComposition>()
        //这里是一个死循环 是依靠协程来挂起恢复的
        while (shouldKeepRecomposing) {
            awaitWorkAvailable()

            // Don't await a new frame if we don't have frame-scoped work
            if (
                synchronized(stateLock) {
                    if (!hasFrameWorkLocked) {
                      //这里是把snapshot的变化丢给每一个composition,然后composition判断它的observations需不需要刷新这个值,最终会丢到Recomposer的compositionInvalidations里
                      //composition的observations是composing的时候的MutableSnapshot的readObserver,也就是代码中read了MutableState的地方
                        recordComposerModificationsLocked()
                        !hasFrameWorkLocked
                    } else false
                }
            ) continue

            // Align work with the next frame to coalesce changes.
            // Note: it is possible to resume from the above with no recompositions pending,
            // instead someone might be awaiting our frame clock dispatch below.
            // We use the cached frame clock from above not just so that we don't locate it
            // each time, but because we've installed the broadcastFrameClock as the scope
            // clock above for user code to locate.
            parentFrameClock.withFrameNanos { frameTime ->
                // Dispatch MonotonicFrameClock frames first; this may produce new
                // composer invalidations that we must handle during the same frame.
                if (broadcastFrameClock.hasAwaiters) {
                    trace("Recomposer:animation") {
                        // Propagate the frame time to anyone who is awaiting from the
                        // recomposer clock.
                        broadcastFrameClock.sendFrame(frameTime)

                        // Ensure any global changes are observed
                        Snapshot.sendApplyNotifications()
                    }
                }

                trace("Recomposer:recompose") {
                    // Drain any composer invalidations from snapshot changes and record
                    // composers to work on
                    synchronized(stateLock) {
                        recordComposerModificationsLocked()
                      //然后上一步的compositionInvalidations加到toRecompose
                        compositionInvalidations.fastForEach { toRecompose += it }
                        compositionInvalidations.clear()
                    }

                    // Perform recomposition for any invalidated composers
                    val modifiedValues = IdentityArraySet<Any>()
                    val alreadyComposed = IdentityArraySet<ControlledComposition>()
                    while (toRecompose.isNotEmpty()) {
                        try {
                            toRecompose.fastForEach { composition ->
                                alreadyComposed.add(composition)
                                //实际recompose的地方,生成changes
                                performRecompose(composition, modifiedValues)?.let {
                                    toApply += it
                                }
                            }
                        } finally {
                            toRecompose.clear()
                        }

                        // Find any trailing recompositions that need to be composed because
                        // of a value change by a composition. This can happen, for example, if
                        // a CompositionLocal changes in a parent and was read in a child
                        // composition that was otherwise valid.
                      //recompose过程中的值变化也要加入toRecompose再recompose
                        if (modifiedValues.isNotEmpty()) {
                            synchronized(stateLock) {
                                knownCompositions.fastForEach { value ->
                                    if (
                                        value !in alreadyComposed &&
                                        value.observesAnyOf(modifiedValues)
                                    ) {
                                        toRecompose += value
                                    }
                                }
                            }
                        }
                    }

                    if (toApply.isNotEmpty()) {
                        changeCount++

                        // Perform apply changes
                        try {
                            toApply.fastForEach { composition ->
                                //最后和初始化一样的调用composition.applyChanges(),将changes合到SlotTable里,并且操作实际节点的关系
                                composition.applyChanges()
                            }
                        } finally {
                            toApply.clear()
                        }
                    }

                    synchronized(stateLock) {
                        deriveStateLocked()
                    }
                }
            }
        }
    }

 private suspend fun awaitWorkAvailable() {
   //没事干就挂起了,然后保存继续的续体到workContinuation
        if (!hasSchedulingWork) {
            suspendCancellableCoroutine<Unit> { co ->
                synchronized(stateLock) {
                    if (hasSchedulingWork) {
                        co.resume(Unit)
                    } else {
                        workContinuation = co
                    }
                }
            }
        }
    }

//这个是恢复死循环的方法
private fun deriveStateLocked(): CancellableContinuation<Unit>? {
        if (_state.value <= State.ShuttingDown) {
            knownCompositions.clear()
            snapshotInvalidations.clear()
            compositionInvalidations.clear()
            compositionsAwaitingApply.clear()
            workContinuation?.cancel()
            workContinuation = null
            return null
        }
  
  //判断有没有事要干
        val newState = when {
            runnerJob == null -> {
                snapshotInvalidations.clear()
                compositionInvalidations.clear()
                if (broadcastFrameClock.hasAwaiters) State.InactivePendingWork else State.Inactive
            }
            compositionInvalidations.isNotEmpty() ||
                snapshotInvalidations.isNotEmpty() ||
                compositionsAwaitingApply.isNotEmpty() ||
                concurrentCompositionsOutstanding > 0 ||
                broadcastFrameClock.hasAwaiters -> State.PendingWork
            else -> State.Idle
        }
  //有事干就继续之前保存的续体,没事干就继续挂着
        _state.value = newState
        return if (newState == State.PendingWork) {
            workContinuation.also {
                workContinuation = null
            }
        } else null
    }

@OptIn(ExperimentalComposeApi::class)
    private suspend fun recompositionRunner(
        block: suspend CoroutineScope.(parentFrameClock: MonotonicFrameClock) -> Unit
    ) {
        val parentFrameClock = coroutineContext.monotonicFrameClock
        withContext(broadcastFrameClock) {
            // Enforce mutual exclusion of callers; register self as current runner
            val callingJob = coroutineContext.job
            registerRunnerJob(callingJob)

            // 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) {
                        snapshotInvalidations += changed
                      //这里接收到变化也会试图去继续循环
                        deriveStateLocked()
                    } else null
                }?.resume(Unit)
            }
...
    }
      
private fun performRecompose(
        composition: ControlledComposition,
        modifiedValues: IdentityArraySet<Any>?
    ): ControlledComposition? {
        if (composition.isComposing || composition.isDisposed) return null
        return if (
            composing(composition, modifiedValues) {
                if (modifiedValues?.isNotEmpty() == true) {
                    // Record write performed by a previous composition as if they happened during
                    // composition.
                    composition.prepareCompose {
                        modifiedValues.forEach { composition.recordWriteOf(it) }
                    }
                }
                composition.recompose()
            }
        ) composition else null
    }
      
override fun recompose(): Boolean = synchronized(lock) {
        drainPendingModificationsForCompositionLocked()
        composer.recompose(takeInvalidations()).also { shouldDrain ->
            // Apply would normally do this for us; do it now if apply shouldn't happen.
            if (!shouldDrain) drainPendingModificationsLocked()
        }
    }
      
internal fun recompose(
        invalidationsRequested: IdentityArrayMap<RecomposeScopeImpl, IdentityArraySet<Any>?>
    ): Boolean {
        runtimeCheck(changes.isEmpty()) { "Expected applyChanges() to have been called" }
        // even if invalidationsRequested is empty we still need to recompose if the Composer has
        // some invalidations scheduled already. it can happen when during some parent composition
        // there were a change for a state which was used by the child composition. such changes
        // will be tracked and added into `invalidations` list.
        if (invalidationsRequested.isNotEmpty() || invalidations.isNotEmpty()) {
          //最后又是走到了ComposerImpl.doCompose,和初始化的时候区别就是这次的content为null,然后doCompose生成了changes
            doCompose(invalidationsRequested, null)
            return changes.isNotEmpty()
        }
        return false
    }

在这里插入图片描述
可以看到试图继续死循环的地方相当多

MutableSnapshot中
private inline fun <T> composing(
        composition: ControlledComposition,
        modifiedValues: IdentityArraySet<Any>?,
        block: () -> T
    ): T {
  //回到Composition.composing方法,在初始化和recompose都会调用
  //抓取了一个MutableSnapshot并且注册了监听
        val snapshot = Snapshot.takeMutableSnapshot(
            readObserverOf(composition), writeObserverOf(composition, modifiedValues)
        )
        try {
            return snapshot.enter(block)
        } finally {
            applyAndCheck(snapshot)
        }
    }

private fun applyAndCheck(snapshot: MutableSnapshot) {
  //snapshot.apply中也会通知到applyObservers,之后的流程是一样的
        val applyResult = snapshot.apply()
        if (applyResult is SnapshotApplyResult.Failure) {
            error(
                "Unsupported concurrent change during composition. A state object was " +
                    "modified by composition as well as being modified outside composition."
            )
            // TODO(chuckj): Consider lifting this restriction by forcing a recompose
        }
    }

基于Android View的Compose

一个别的地方看到的例子

@Composable
fun TextView(
    text: String,
    onClick: () -> Unit = {}
) {
    val context = LocalContext.current
    ComposeNode<TextView, ViewApplier>(
        factory = {
            TextView(context)
        },
        update = {
            set(text) {
                this.text = text
            }
            set(onClick) {
                setOnClickListener { onClick() }
            }
        },
    )
}

@Composable
fun LinearLayout(children: @Composable () -> Unit) {
    val context = LocalContext.current
    ComposeNode<LinearLayout, ViewApplier>(
        factory = {
            LinearLayout(context).apply {
                orientation = LinearLayout.VERTICAL
                layoutParams = ViewGroup.LayoutParams(
                    ViewGroup.LayoutParams.MATCH_PARENT,
                    ViewGroup.LayoutParams.MATCH_PARENT,
                )
            }
        },
        update = {},
        content = children,
    )
}

class ViewApplier(val view: FrameLayout) : AbstractApplier<View>(view) {
    override fun onClear() {
        (view as? ViewGroup)?.removeAllViews()
    }

    override fun insertBottomUp(index: Int, instance: View) {
        (current as? ViewGroup)?.addView(instance, index)
    }

    override fun insertTopDown(index: Int, instance: View) {
    }

    override fun move(from: Int, to: Int, count: Int) {
        // NOT Supported
        TODO()
    }

    override fun remove(index: Int, count: Int) {
        (view as? ViewGroup)?.removeViews(index, count)
    }
}

@Composable
fun AndroidViewApp() {
    var count by remember { mutableStateOf(1) }
    LinearLayout {
        TextView(
            text = "This is the Android TextView!!",
        )
        repeat(count) {
            TextView(
                text = "Android View!!TextView:$it $count",
                onClick = {
                    count++
                }
            )
        }
    }
}

private fun runApp(context: Context): FrameLayout {
        val composer = Recomposer(Dispatchers.Main)
        val channel = Channel<Unit>(Channel.CONFLATED)
        CoroutineScope(Dispatchers.Main).launch {
            channel.consumeEach {
                Snapshot.sendApplyNotifications()
            }
        }
        Snapshot.registerGlobalWriteObserver {
            channel.trySend(Unit)
        }
        val mainScope = MainScope()
        mainScope.launch(start = CoroutineStart.UNDISPATCHED) {
            withContext(coroutineContext + DefaultMonotonicFrameClock) {
                composer.runRecomposeAndApplyChanges()
            }
        }
        mainScope.launch {
            composer.state.collect {
                println("composer:$it")
            }
        }

        val rootDocument = FrameLayout(context)
        Composition(ViewApplier(rootDocument), composer).apply {
            setContent {
                CompositionLocalProvider(LocalContext provides context) {
                    AndroidViewApp()
                }
            }
        }
        return rootDocument
    }

以上,TextView和LinearLayout两个方法是创建ComposeNode的方法,ViewApplier操作实际的ViewGroup树。

最后在runApp方法中,是一个Compose系统需要的最少的东西,一个Recomposer,一个跑recompose的地方,一个Composition,还需要初始化GlobalSnapshot的监听(也可以没有,就是不能在@Composable之外的地方刷新),不操作实际Node连Applier都可以是个空的

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值