本文仅探究Google Compose原理 不包含ui绘制实现
Compose原理
先看as compose项目模板生成的基础demo
kotlin
class MainActivity : ComponentActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
enableEdgeToEdge()
setContent {
MyApplicationTheme {
Scaffold(modifier = Modifier.fillMaxSize()) { innerPadding ->
Greeting(
name = "Android",
modifier = Modifier.padding(innerPadding)
)
}
}
}
@Composable
fun Greeting(name: String, modifier: Modifier = Modifier) {
var currentTime by remember { mutableStateOf(System.currentTimeMillis()) }
LaunchedEffect(Unit) {
while (true) {
currentTime = System.currentTimeMillis()
delay(1000)
Log.e("zjw", currentTime.hashCode().toString())
}
}
val timeFormat = remember { SimpleDateFormat("HH:mm:ss", Locale.getDefault()) }
val timeStr = timeFormat.format(Date(currentTime))
Text(
text = "Hello $name! Current Time: $timeStr",
modifier = modifier
)
}
很简单的代码不做细究,要探究Compose重组UI逻辑,先上反编译手段看看@Composable Greeting会变成什么 jadx工具反编译MainActivity


setContentView设置了一个ComposeView 这就是Compose接入到android View系统的桥梁了,同时Compose接入



这里省略部分调用细节,简单来说就是ComponentActivity setContent不光给android.R.id.content添加了子ComposeView还添加一个Choreographer.FrameCallback和recompose机制绑定
注意到有一个lambda ComponentActivityKt.setContent$default(this, null, ComposableSingletons$MainActivityKt.INSTANCE.m8049getLambda3$app_debug
jadx双击跳转到这个lambda方法所在源码继续看ComposableSingletons$MainActivityKt
php
public final class ComposableSingletons$MainActivityKt {
public static final ComposableSingletons$MainActivityKt INSTANCE = new ComposableSingletons$MainActivityKt();
/* renamed from: lambda-1 reason: not valid java name */
public static Function3<PaddingValues, Composer, Integer, Unit> f79lambda1 = ComposableLambdaKt.composableLambdaInstance(1855199361, false, new Function3<PaddingValues, Composer, Integer, Unit>() { // from class: com.zjw.compose.myapplication.ComposableSingletons$MainActivityKt$lambda-1$1
@Override // kotlin.jvm.functions.Function3
public /* bridge */ /* synthetic */ Unit invoke(PaddingValues paddingValues, Composer composer, Integer num) {
invoke(paddingValues, composer, num.intValue());
return Unit.INSTANCE;
}
public final void invoke(PaddingValues innerPadding, Composer $composer, int $changed) {
Intrinsics.checkNotNullParameter(innerPadding, "innerPadding");
ComposerKt.sourceInformation($composer, "C42@1665L139:MainActivity.kt#72esb0");
int $dirty = $changed;
if (($changed & 6) == 0) {
$dirty |= $composer.changed(innerPadding) ? 4 : 2;
}
if (($dirty & 19) != 18 || !$composer.getSkipping()) {
if (ComposerKt.isTraceInProgress()) {
ComposerKt.traceEventStart(1855199361, $dirty, -1, "com.zjw.compose.myapplication.ComposableSingletons$MainActivityKt.lambda-1.<anonymous> (MainActivity.kt:42)");
}
MainActivityKt.Greeting("Android", PaddingKt.padding(Modifier.INSTANCE, innerPadding), $composer, 6, 0);
if (ComposerKt.isTraceInProgress()) {
ComposerKt.traceEventEnd();
return;
}
return;
}
$composer.skipToGroupEnd();
}
});
/* renamed from: lambda-2 reason: not valid java name */
public static Function2<Composer, Integer, Unit> f80lambda2 = ComposableLambdaKt.composableLambdaInstance(184410672, false, new Function2<Composer, Integer, Unit>() { // from class: com.zjw.compose.myapplication.ComposableSingletons$MainActivityKt$lambda-2$1
@Override // kotlin.jvm.functions.Function2
public /* bridge */ /* synthetic */ Unit invoke(Composer composer, Integer num) {
invoke(composer, num.intValue());
return Unit.INSTANCE;
}
public final void invoke(Composer $composer, int $changed) {
ComposerKt.sourceInformation($composer, "C41@1583L239:MainActivity.kt#72esb0");
if (($changed & 3) != 2 || !$composer.getSkipping()) {
if (ComposerKt.isTraceInProgress()) {
ComposerKt.traceEventStart(184410672, $changed, -1, "com.zjw.compose.myapplication.ComposableSingletons$MainActivityKt.lambda-2.<anonymous> (MainActivity.kt:41)");
}
ScaffoldKt.m2866ScaffoldTvnljyQ(SizeKt.fillMaxSize$default(Modifier.INSTANCE, 0.0f, 1, null), null, null, null, null, 0, 0L, 0L, null, ComposableSingletons$MainActivityKt.INSTANCE.m8047getLambda1$app_debug(), $composer, 805306374, 510);
if (ComposerKt.isTraceInProgress()) {
ComposerKt.traceEventEnd();
return;
}
return;
}
$composer.skipToGroupEnd();
}
});
/* renamed from: lambda-3 reason: not valid java name */
public static Function2<Composer, Integer, Unit> f81lambda3 = ComposableLambdaKt.composableLambdaInstance(749891428, false, new Function2<Composer, Integer, Unit>() { // from class: com.zjw.compose.myapplication.ComposableSingletons$MainActivityKt$lambda-3$1
@Override // kotlin.jvm.functions.Function2
public /* bridge */ /* synthetic */ Unit invoke(Composer composer, Integer num) {
invoke(composer, num.intValue());
return Unit.INSTANCE;
}
public final void invoke(Composer $composer, int $changed) {
ComposerKt.sourceInformation($composer, "C40@1546L290:MainActivity.kt#72esb0");
if (($changed & 3) != 2 || !$composer.getSkipping()) {
if (ComposerKt.isTraceInProgress()) {
ComposerKt.traceEventStart(749891428, $changed, -1, "com.zjw.compose.myapplication.ComposableSingletons$MainActivityKt.lambda-3.<anonymous> (MainActivity.kt:40)");
}
ThemeKt.MyApplicationTheme(false, false, ComposableSingletons$MainActivityKt.INSTANCE.m8048getLambda2$app_debug(), $composer, 384, 3);
if (ComposerKt.isTraceInProgress()) {
ComposerKt.traceEventEnd();
return;
}
return;
}
$composer.skipToGroupEnd();
}
});
/* renamed from: lambda-4 reason: not valid java name */
public static Function2<Composer, Integer, Unit> f82lambda4 = ComposableLambdaKt.composableLambdaInstance(1718125071, false, new Function2<Composer, Integer, Unit>() { // from class: com.zjw.compose.myapplication.ComposableSingletons$MainActivityKt$lambda-4$1
@Override // kotlin.jvm.functions.Function2
public /* bridge */ /* synthetic */ Unit invoke(Composer composer, Integer num) {
invoke(composer, num.intValue());
return Unit.INSTANCE;
}
public final void invoke(Composer $composer, int $changed) {
ComposerKt.sourceInformation($composer, "C132@3832L19:MainActivity.kt#72esb0");
if (($changed & 3) == 2 && $composer.getSkipping()) {
$composer.skipToGroupEnd();
return;
}
if (ComposerKt.isTraceInProgress()) {
ComposerKt.traceEventStart(1718125071, $changed, -1, "com.zjw.compose.myapplication.ComposableSingletons$MainActivityKt.lambda-4.<anonymous> (MainActivity.kt:132)");
}
MainActivityKt.Greeting("Android", null, $composer, 6, 2);
if (ComposerKt.isTraceInProgress()) {
ComposerKt.traceEventEnd();
}
}
});
/* renamed from: getLambda-1$app_debug reason: not valid java name */
public final Function3<PaddingValues, Composer, Integer, Unit> m8047getLambda1$app_debug() {
return f79lambda1;
}
/* renamed from: getLambda-2$app_debug reason: not valid java name */
public final Function2<Composer, Integer, Unit> m8048getLambda2$app_debug() {
return f80lambda2;
}
/* renamed from: getLambda-3$app_debug reason: not valid java name */
public final Function2<Composer, Integer, Unit> m8049getLambda3$app_debug() {
return f81lambda3;
}
}
其实也就是ComposableSingletons$MainActivityKt这个类下面几个Function2 xxxlambda调用,先说结论每个@Composeable方法编译后对应一个Function2<Composer, Integer, Unit>类型xxxlambda,
MainActivity 调用setContent的lambda函数 反编译ComposableSingletons <math xmlns="http://www.w3.org/1998/Math/MathML"> M a i n A c t i v i t y K t . I N S T A N C E . m 8049 g e t L a m b d a 3 MainActivityKt.INSTANCE.m8049getLambda3 </math>MainActivityKt.INSTANCE.m8049getLambda3app_debug 对应ComposableSingletons <math xmlns="http://www.w3.org/1998/Math/MathML"> M a i n A c t i v i t y K t 类 81 l a m b d a 3 − > 调用 i n v o k e 方法中的 C o m p o s a b l e S i n g l e t o n s MainActivityKt类 81lambda3 -> 调用invoke方法中的 ComposableSingletons </math>MainActivityKt类81lambda3−>调用invoke方法中的ComposableSingletonsMainActivityKt.INSTANCE.m8048getLambda2$app_debug()对应 f80lambda2调用invoke 不再继续
其实对应就是compose的嵌套调用过程
ini
setContent { //f81lambda3
MyApplicationTheme { //f80lambda2
Scaffold(modifier = Modifier.fillMaxSize()) {//f79lambda1 innerPadding ->
Greeting(
name = "Android",
modifier = Modifier.padding(innerPadding)
)
}
}
}
接下来来看一下Greeting方法会变成什么样
kotlin
public final class MainActivityKt {
/* JADX INFO: Access modifiers changed from: private */
public static final Unit Greeting$lambda$5(String str, Modifier modifier, int i, int i2, Composer composer, int i3) {
Greeting(str, modifier, composer, RecomposeScopeImplKt.updateChangedFlags(i | 1), i2);
return Unit.INSTANCE;
}
/* JADX INFO: Access modifiers changed from: private */
public static final Unit GreetingPreview$lambda$6(int i, Composer composer, int i2) {
GreetingPreview(composer, RecomposeScopeImplKt.updateChangedFlags(i | 1));
return Unit.INSTANCE;
}
public static final void Greeting(final String name, Modifier modifier, Composer $composer, final int $changed, final int i) {
Object obj;
int i2;
int $dirty;
final Modifier modifier2;
Intrinsics.checkNotNullParameter(name, "name");
Composer $composer2 = $composer.startRestartGroup(-1745234003);
ComposerKt.sourceInformation($composer2, "C(Greeting)P(1)103@3008L55,104@3089L117,104@3068L138,110@3228L62,113@3351L93,119@3475L50,124@3688L11,118@3450L277:MainActivity.kt#72esb0");
int $dirty2 = $changed;
if ((i & 1) != 0) {
$dirty2 |= 6;
} else if (($changed & 6) == 0) {
$dirty2 |= $composer2.changed(name) ? 4 : 2;
}
int i3 = i & 2;
if (i3 != 0) {
$dirty2 |= 48;
obj = modifier;
} else if (($changed & 48) == 0) {
obj = modifier;
$dirty2 |= $composer2.changed(obj) ? 32 : 16;
} else {
obj = modifier;
}
if (($dirty2 & 19) != 18 || !$composer2.getSkipping()) {
Modifier.Companion modifier3 = i3 != 0 ? Modifier.INSTANCE : obj;
if (ComposerKt.isTraceInProgress()) {
ComposerKt.traceEventStart(-1745234003, $dirty2, -1, "com.zjw.compose.myapplication.Greeting (MainActivity.kt:102)");
}
$composer2.startReplaceGroup(293979466);
ComposerKt.sourceInformation($composer2, "CC(remember):MainActivity.kt#9igjgp");
Object rememberedValue = $composer2.rememberedValue();
if (rememberedValue == Composer.INSTANCE.getEmpty()) {
i2 = 0;
Object mutableStateOf$default = SnapshotStateKt.mutableStateOf$default(Long.valueOf(System.currentTimeMillis()), null, 2, null);
$composer2.updateRememberedValue(mutableStateOf$default);
rememberedValue = mutableStateOf$default;
} else {
i2 = 0;
}
MutableState currentTime$delegate = (MutableState) rememberedValue;
$composer2.endReplaceGroup();
Unit unit = Unit.INSTANCE;
$composer2.startReplaceGroup(293982120);
ComposerKt.sourceInformation($composer2, "CC(remember):MainActivity.kt#9igjgp");
Object rememberedValue2 = $composer2.rememberedValue();
if (rememberedValue2 == Composer.INSTANCE.getEmpty()) {
$dirty = $dirty2;
Object obj2 = (Function2) new MainActivityKt$Greeting$1$1(currentTime$delegate, null);
$composer2.updateRememberedValue(obj2);
rememberedValue2 = obj2;
} else {
$dirty = $dirty2;
}
$composer2.endReplaceGroup();
EffectsKt.LaunchedEffect(unit, (Function2) rememberedValue2, $composer2, 6);
$composer2.startReplaceGroup(293986513);
ComposerKt.sourceInformation($composer2, "CC(remember):MainActivity.kt#9igjgp");
Object rememberedValue3 = $composer2.rememberedValue();
if (rememberedValue3 == Composer.INSTANCE.getEmpty()) {
Object simpleDateFormat = new SimpleDateFormat("HH:mm:ss", Locale.getDefault());
$composer2.updateRememberedValue(simpleDateFormat);
rememberedValue3 = simpleDateFormat;
}
SimpleDateFormat timeFormat = (SimpleDateFormat) rememberedValue3;
$composer2.endReplaceGroup();
String timeStr = timeFormat.format(new Date(Greeting$lambda$1(currentTime$delegate)));
TextKt.m3151Text4IGK_g("Hello " + name + "! Current Time: " + timeStr, modifier3, 0L, 0L, (FontStyle) null, (FontWeight) null, (FontFamily) null, 0L, (TextDecoration) null, (TextAlign) null, 0L, 0, false, 0, 0, (Function1<? super TextLayoutResult, Unit>) null, (TextStyle) null, $composer2, $dirty & 112, 0, 131068);
modifier2 = modifier3;
$composer2 = $composer2;
ScopeUpdateScope endRestartGroup = $composer2.endRestartGroup();
if (endRestartGroup != null) {
endRestartGroup.updateScope(new Function2() { // from class: com.zjw.compose.myapplication.MainActivityKt$$ExternalSyntheticLambda1
@Override // kotlin.jvm.functions.Function2
public final Object invoke(Object obj3, Object obj4) {
Unit Greeting$lambda$5;
Greeting$lambda$5 = MainActivityKt.Greeting$lambda$5(name, modifier2, $changed, i, (Composer) obj3, ((Integer) obj4).intValue());
return Greeting$lambda$5;
}
});
}
}
public static final void Greeting(final String name, Modifier modifier, Composer $composer, final int $changed, final int i) {
其实大概意思就是 通过入参 <math xmlns="http://www.w3.org/1998/Math/MathML"> c h a n g e d 与位运算结合 changed与位运算 结合 </math>changed与位运算结合composer2.changed(obj)判断是否dirty ,然后
ruby
if(dirty){
//调用该composable lambda中invoke 逻辑
TextKt.m3151Text4IGK_g("Hello " + name + "! Current Time: " ...
}else{
$composer2.skipToGroupEnd()
}
这里 $composer2就是 Composer类型,它有个changed方法负责判断composeable方法入参是否发生变化,如果发生变化就走composeable invoke逻辑发生重组或者说重绘,否则就skipToGroupEnd 不发生重组,这也是compose保证高效的原理之一 参数不发生变化不进行重组
以demo中 text文本发生变化来具体探究compose提供的基础组件 Text是怎么重组的
ini
Text(
text = "Hello $name! Current Time: $timeStr",
modifier = modifier
)
//Text 这里被翻译成 TextKt.m3151Text4IGK_g("Hello " + name + "! Current Time: " ...
kotlin
/* compiled from: Text.kt */
package androidx.compose.material3;
/* renamed from: Text--4IGK_g reason: not valid java name */
public static final void m3151Text4IGK_g(final String text, Modifier modifier, long color, long fontSize, FontStyle fontStyle, FontWeight fontWeight, FontFamily fontFamily, long letterSpacing, TextDecoration textDecoration, TextAlign textAlign, long lineHeight, int overflow, boolean softWrap, int maxLines, int minLines, Function1<? super TextLayoutResult, Unit> function1, TextStyle style, Composer $composer, final int $changed, final int $changed1, final int i) {
Object obj;
Modifier modifier2;
int i2;
long color2;
long fontSize2;
Object fontStyle2;
Object fontWeight2;
int $dirty;
int $dirty1;
int i3;
int i4;
int $dirty12;
int i5;
boolean softWrap2;
int $dirty13;
TextAlign textAlign2;
int overflow2;
int maxLines2;
int minLines2;
Function1 onTextLayout;
FontFamily fontFamily2;
TextDecoration textDecoration2;
TextStyle style2;
FontStyle fontStyle3;
long fontSize3;
FontWeight fontWeight3;
long letterSpacing2;
long lineHeight2;
int $dirty2;
long textColor;
Composer $composer2;
final boolean softWrap3;
final long color3;
final Modifier modifier3;
final TextAlign textAlign3;
final int maxLines3;
final Function1 onTextLayout2;
final int maxLines4;
final TextStyle style3;
final FontWeight fontWeight4;
final FontStyle fontStyle4;
final FontFamily fontFamily3;
final long letterSpacing3;
final TextDecoration textDecoration3;
final long lineHeight3;
final int minLines3;
final long fontSize4;
int i6;
Composer $composer3 = $composer.startRestartGroup(-2055108902);
ComposerKt.sourceInformation($composer3, "C(Text)P(14,9,0:c#ui.graphics.Color,2:c#ui.unit.TextUnit,3:c#ui.text.font.FontStyle,4!1,5:c#ui.unit.TextUnit,16,15:c#ui.text.style.TextAlign,6:c#ui.unit.TextUnit,11:c#ui.text.style.TextOverflow,12)108@5620L7,113@5732L530:Text.kt#uh7d8r");
int $dirty3 = $changed;
if ((i & 1) != 0) {
$dirty3 |= 6;
obj = text;
} else if (($changed & 6) == 0) {
obj = text;
$dirty3 |= $composer3.changed(obj) ? 4 : 2;
} else {
obj = text;
}
//省略后续代码
翻译一下就是 把入参text传入 $composer3.changed判断是否与上次记录的值发生变化 Composer源码如下



上次text的值就存在Composer里成员变量slotTable对象的slots变量中,值怎么存的可以在SlotTable set方法打断点

可以看到slots中存储了整个ComposeView中所有入参值是以Array数组的形式,这里就涉及到Composer changed方法为什么要调用nextSlot()和Composerable方法反编译后为什么都有如下3个方法调用
ini
Composer $composer2 = $composer.startRestartGroup(-1745234003)
//省略代码
$composer.skipToGroupEnd()
//省略代码
ScopeUpdateScope endRestartGroup = $composer2.endRestartGroup();
因为SlotTable存储的是整个ComposeView组成composeable函数UI树的所有入参Array,扁平化存储,所以需要以Composeable要是使用Group分组,使用"指针"来锚定当前执行到的composeable函数当前入参,指针寻址这里不做展开说明有兴趣可以自行去看,Composer::changed方法判断本次value和slots存储的对应text是否变化,变化就发生重组逻辑。
至此我们就已经基本探究完Compose机制了,总结一下就是Compose是使用开发者写的@Composeable函数关系来描述这个UI树的,不同于Android View系统使用的ViewGroup/View对象Tree来描述UI树;而UI的更新 Compose使用的入参判断变化来更新,而Android View系统使用的是命令式比如显示调用View.requstLayout
Compose实现
原理搞懂了,接下来就来实现一个Compose,其实就5个核心类,让AI直接给出Compose简易实现,这里就不贴代码了,结尾有Github Compose简易实现源码链接,以下是核心类功能介绍
一、SlotTable
功能:
- 核心"记忆仓库",线性存储所有 Composable 的状态(remember 值、参数值、lambda 等)
- 管理 index 指针,支持顺序访问
- 管理 Group:startGroup / endGroup / skipGroup
- startGroup:记录 group 起始 index
- endGroup:计算 group 占用 slot 数量
- skipGroup:重组时快速跳过整个 group
原理:
- 每次调用 next() → 返回当前 slot 并 index++
- 每次调用 update() → 修改 index-1 对应的 slot
二、Composer
功能:
- SlotTable 封装器:提供更高层的接口
- changed(value) → 判断当前 slot 是否和传入值不同,返回 Boolean
- remember(factory) → 读取 slot,如果为空执行 lambda 并写入 slot
- Group 管理:startGroup() / endGroup() / skipGroup()
原理:
- 将 Composable 函数的运行与 SlotTable 状态绑定
- 控制 skip / 重组逻辑
三、State
功能:
- 可变状态持有者
- value 变化时触发回调 → 触发重组
原理:
- 内部存储当前值 _value
- set(value) → 值改变 → 调用 onChange()
- mutableStateOf 是创建 State 的工厂函数
四、Composition
功能:
- 管理整个 Composable 树的入口
- setContent() → 设置顶层 Composable
- recompose() → 重组整个树
- 内部持有 SlotTable + Composer
原理:
- 每次 recompose() → reset SlotTable → 执行顶层 Composable → 根据 changed/skip 更新 slot
- 可由 State 改变触发
五、Composable 函数
功能:
- 业务逻辑 + UI 表达
- 使用 composer.changed() 判断参数是否变化
Android View系统 对比 Compose
| 特性 | View 系统 | Compose |
|---|---|---|
| UI架构 | View对象组成的UI Tree | Composable 函数描述UI Tree |
| 渲染对象 | 每个 View 占用对象内存 | SlotTable 记录状态、重组时决定是否绘制 |
| Draw / Canvas | 每个 View 负责绘制自己 | Applier(类似 Canvas)统一提交 UI 树变更 |
| 重绘策略 | requestLayout/invalidate() → 递归绘制 | State 改变 → Recomposer → 重组 → 重绘 |