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层其实是kotlin的Compiler plugin
composeOptions {
kotlinCompilerExtensionVersion compose_version
kotlinCompilerVersion '1.5.10'
}
虽然Compose在我们一般使用中的表现似乎就只是一个@Composable的注解,但是处理这个注解的并不是一个apt,而是一个Compiler plugin。这个@Composable注解某种意义上不能算是一个普通的注解,主要原因还是因为Compiler plugin和apt能做的事情差太多了。
这个太难了我也没有深入学习,参考资料在此
另外谷歌官方还有一个推荐用来替代apt的东西,Kotlin Symbol Processing (KSP) KSP是用来开发轻量级插件,速度快,也有文档,不用关心kotlin版本变化,虽然功能上比完整Compiler插件少一点但是也很强大,有兴趣可以看看。
上张图,有兴趣的自己看吧
首先从概念上来说,@Composable的方法的意义是发射一个节点,不管是UI节点或者是别的数据节点,这个从下面的反编译的结果也是可以看到的。
跟平常使用没什么关系
禁止标注的方法内部调用@Composable方法,用于防止inline方法里的lambda方法参数发射节点。举个例子最常用的remember方法
@Composable
inline fun <T> remember(calculation: @DisallowComposableCalls () -> T): T =
currentComposer.cache(false, calculation)
不生成节点只读的@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;
}
标记的方法生成的结果不可重启/跳过
@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也没有用到
定义略有不同,但都是标记纯函数或者不可变成员等等帮助Compiler优化recompose过程
@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层。
Applier接口:操作生成的Node,是使得生成的节点产生管理的地方
@Composable:标注产生group的方法
Composer接口:直接操作SlotTable和Applier的类,唯一实现ComposerImpl
Composition接口:有一个继承的接口ControlledComposition,ControlledComposition有一个Runtime层唯一实现CompositionImpl,用于连接CompositionContext和Composer,持有一个composable,是setContent和applyChanges发生的地方
CompositionLocal:Composition的本地变量,注册之后用provide提供,用current拿,会在编译的时候生成代码
Effects:安全保存side-effect的一些方法。side-effect指的是一些可能会修改全局变量或者读写IO的方法产生的副作用,@Composable方法应该都是无副作用的,因为@Composable方法会运行很多次,而且也不好准确预测一定会运行几次,所以这种side-effect需要用effects里的方法操作一下。
MutableState&Snapshot:常用的MutableState是一个继承SnapshotMutableState,SnapshotMutableState和Snapshot是结合使用的,state是用来向Snapshot读写一个值,Snapshot有两个作用,一个是可以订阅读写的observer,另一个是版本隔离,可以切一个分支Snapshot出来然后在分支Shapshot上修改再apply到主的Snapshot,并且可以定义冲突的policy,这个也支持recompose可以并发的运行。
PausableMonotonicFrameClock:可以pause和resume的FrameClock,animation都是依靠这个FrameClock,对于Android而言,就是包了一层AndroidUiFrameClock,最后还是调用的Choreographer.FrameCallback
CompositionContext&Recomposer&CompositionContextImpl:Recomposer和CompositionContextImpl是两种实现。Recomposer是一颗树对应的唯一一个控制整体recompose流程的东西,存了所有的compositions。CompositionContextImpl是ComposerImpl的内部类,存了一些composers,主要是SubCompose用(LazyList之类的)。
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插入过程(因为我看不懂)
移除的过程更简单,先把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决定要不要替换等等,实际的过程比上面的复杂很多。
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流程是分析因为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,上面提到过,在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
}
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
}
}
一个别的地方看到的例子
@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都可以是个空的