返回

compose逆向分析1

2022-12-13 by zhangyong

compose相关问题思考

compose函数为什么只能在compose函数中调用

Compose不是一个注解处理器。Compose 在 Kotlin 编译器的类型检测与代码生成阶段依赖 Kotlin 编译器插件工作,所以无需注解处理器即可使用 Compose。
这一注解更接近于一个语言关键字。作为类比,可以参考 Kotlin 的 suspend 关键 字。

Kotlin 的 suspend 关键字 适用于处理函数类型:您可以将函数、lambda 或者函数类型声明为 suspend。Compose 与其工作方式相同:它可以改变函数类型。

这里的重点是,当您使用 @Composable 注解一个函数类型时,会导致它类型的改变:未被注解的相同函数类型与注解后的类型互不兼容。同样的,挂起 (suspend) 函数需要调用上下文作为参数,这意味着您只能在其他挂起函数中调用挂起函数。

通过逆向解析compose的状态变化

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
@Preview
@Composable
fun Counter() {
Column() {
println("1111")
var count by remember { mutableStateOf(0) }
println("2222")
Button(
onClick = {
count++
}
) {
println("3333")
Text(
text = "$count"
)
}
println("4444")
}
}

启动输出

1
2
3
4
I/System.out: 1111
I/System.out: 2222
I/System.out: 3333
I/System.out: 4444

点击按钮输出

1
I/System.out: 3333

逆向代码

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
public final class CounterKt {
public static final void Counter(Composer paramComposer, int paramInt) {
Composer composer = paramComposer.startRestartGroup(-2095581453);
ComposerKt.sourceInformation(composer, "C(Counter)17@390L332:Counter.kt#ptgicz");
if (paramInt != 0 || !composer.getSkipping()) {
composer.startReplaceableGroup(-483455358);
ComposerKt.sourceInformation(composer, "C(Column)P(2,3,1)76@3834L61,77@3900L133:Column.kt#2w3rfo");
Modifier modifier = (Modifier)Modifier.Companion;
MeasurePolicy measurePolicy = ColumnKt.columnMeasurePolicy(Arrangement.INSTANCE.getTop(), Alignment.Companion.getStart(), composer, 0 >> 3 & 0xE | 0 >> 3 & 0x70);
composer.startReplaceableGroup(-1323940314);
ComposerKt.sourceInformation(composer, "C(Layout)P(!1,2)74@2915L7,75@2970L7,76@3029L7,77@3041L460:Layout.kt#80mrfh");
CompositionLocal compositionLocal1 = (CompositionLocal)CompositionLocalsKt.getLocalDensity();
ComposerKt.sourceInformationMarkerStart(composer, 2023513938, "C:CompositionLocal.kt#9igjgp");
Object object1 = composer.consume(compositionLocal1);
ComposerKt.sourceInformationMarkerEnd(composer);
object1 = object1;
CompositionLocal compositionLocal2 = (CompositionLocal)CompositionLocalsKt.getLocalLayoutDirection();
ComposerKt.sourceInformationMarkerStart(composer, 2023513938, "C:CompositionLocal.kt#9igjgp");
Object object2 = composer.consume(compositionLocal2);
ComposerKt.sourceInformationMarkerEnd(composer);
object2 = object2;
CompositionLocal compositionLocal3 = (CompositionLocal)CompositionLocalsKt.getLocalViewConfiguration();
ComposerKt.sourceInformationMarkerStart(composer, 2023513938, "C:CompositionLocal.kt#9igjgp");
Object object3 = composer.consume(compositionLocal3);
ComposerKt.sourceInformationMarkerEnd(composer);
object3 = object3;
Function0 function0 = ComposeUiNode.Companion.getConstructor();
Function3 function3 = LayoutKt.materializerOf(modifier);
int i = (0 << 3 & 0x70) << 9 & 0x1C00 | 0x6;
if (!(composer.getApplier() instanceof androidx.compose.runtime.Applier))
ComposablesKt.invalidApplier();
composer.startReusableNode();
if (composer.getInserting()) {
composer.createNode(function0);
} else {
composer.useNode();
}
composer.disableReusing();
Composer composer1 = Updater.constructor-impl(composer);
Updater.set-impl(composer1, measurePolicy, ComposeUiNode.Companion.getSetMeasurePolicy());
Updater.set-impl(composer1, object1, ComposeUiNode.Companion.getSetDensity());
Updater.set-impl(composer1, object2, ComposeUiNode.Companion.getSetLayoutDirection());
Updater.set-impl(composer1, object3, ComposeUiNode.Companion.getSetViewConfiguration());
composer.enableReusing();
function3.invoke(SkippableUpdater.box-impl(SkippableUpdater.constructor-impl(composer)), composer, Integer.valueOf(i >> 3 & 0x70));
composer.startReplaceableGroup(2058660585);
composer.startReplaceableGroup(-1163856341);
ComposerKt.sourceInformation(composer, "C78@3948L9:Column.kt#2w3rfo");
if ((i >> 9 & 0xE & 0xB) != 2 || !composer.getSkipping()) {
ColumnScope columnScope = (ColumnScope)ColumnScopeInstance.INSTANCE;
composer.startReplaceableGroup(-125081623);
ComposerKt.sourceInformation(composer, "C19@446L30,22@539L39,21@509L183:Counter.kt#ptgicz");
if (((0 >> 6 & 0x70 | 0x6) & 0x51) != 16 || !composer.getSkipping()) {
String str = LiveLiterals$CounterKt.INSTANCE.String$arg-0$call-println$fun-$anonymous$$arg-3$call-Column$fun-Counter();
System.out.println(str);
composer.startReplaceableGroup(-492369756);
ComposerKt.sourceInformation(composer, "C(remember):Composables.kt#9igjgp");
Object object = composer.rememberedValue();
if (object == Composer.Companion.getEmpty()) {
object = SnapshotStateKt.mutableStateOf$default(Integer.valueOf(LiveLiterals$CounterKt.INSTANCE.Int$arg-0$call-mutableStateOf$fun-$anonymous$$arg-0$call-remember$val-count$delegate$fun-$anonymous$$arg-3$call-Column$fun-Counter()), null, 2, null);
composer.updateRememberedValue(object);
}
composer.endReplaceableGroup();
object1 = object;
object = LiveLiterals$CounterKt.INSTANCE.String$arg-0$call-println-1$fun-$anonymous$$arg-3$call-Column$fun-Counter();
System.out.println(object);
composer.startReplaceableGroup(1157296644);
ComposerKt.sourceInformation(composer, "C(remember)P(1):Composables.kt#9igjgp");
boolean bool = composer.changed(object1);
object = composer.rememberedValue();
if (bool || object == Composer.Companion.getEmpty()) {
object = new CounterKt$Counter$1$1$1((MutableState<Integer>)object1);
composer.updateRememberedValue(object);
}
composer.endReplaceableGroup();
ButtonKt.Button((Function0)object, null, false, null, null, null, null, null, null, (Function3)ComposableLambdaKt.composableLambda(composer, 1051517913, true, new CounterKt$Counter$1$2((MutableState<Integer>)object1)), composer, 805306368, 510);
object = LiveLiterals$CounterKt.INSTANCE.String$arg-0$call-println-2$fun-$anonymous$$arg-3$call-Column$fun-Counter();
System.out.println(object);
} else {
composer.skipToGroupEnd();
}
composer.endReplaceableGroup();
} else {
composer.skipToGroupEnd();
}
composer.endReplaceableGroup();
composer.endReplaceableGroup();
composer.endNode();
composer.endReplaceableGroup();
composer.endReplaceableGroup();
} else {
composer.skipToGroupEnd();
}
ScopeUpdateScope scopeUpdateScope = composer.endRestartGroup();
if (scopeUpdateScope != null)
scopeUpdateScope.updateScope(new CounterKt$Counter$2(paramInt));
}

private static final int Counter$lambda-4$lambda-1(MutableState<Integer> paramMutableState) {
return ((Number)((State)paramMutableState).getValue()).intValue();
}

private static final void Counter$lambda-4$lambda-2(MutableState<Integer> paramMutableState, int paramInt) {
paramMutableState.setValue(Integer.valueOf(paramInt));
}

@Metadata(k = 3, mv = {1, 7, 1}, xi = 48)
static final class CounterKt$Counter$1$1$1 extends Lambda implements Function0<Unit> {
final MutableState<Integer> $count$delegate;

CounterKt$Counter$1$1$1(MutableState<Integer> param1MutableState) {
super(0);
}

public final void invoke() {
int i = CounterKt.Counter$lambda-4$lambda-1(this.$count$delegate);
CounterKt.Counter$lambda-4$lambda-2(this.$count$delegate, i + 1);
}
}

@Metadata(k = 3, mv = {1, 7, 1}, xi = 48)
static final class CounterKt$Counter$1$2 extends Lambda implements Function3<RowScope, Composer, Integer, Unit> {
final MutableState<Integer> $count$delegate;

CounterKt$Counter$1$2(MutableState<Integer> param1MutableState) {
super(3);
}

public final void invoke(RowScope param1RowScope, Composer param1Composer, int param1Int) {
Intrinsics.checkNotNullParameter(param1RowScope, "$this$Button");
ComposerKt.sourceInformation(param1Composer, "C27@631L51:Counter.kt#ptgicz");
if ((param1Int & 0x51) != 16 || !param1Composer.getSkipping()) {
String str = LiveLiterals$CounterKt.INSTANCE.String$arg-0$call-println$fun-$anonymous$$arg-9$call-Button$fun-$anonymous$$arg-3$call-Column$fun-Counter();
System.out.println(str);
TextKt.Text-fLXpl1I(String.valueOf(CounterKt.Counter$lambda-4$lambda-1(this.$count$delegate)), null, 0L, 0L, null, null, null, 0L, null, null, 0L, 0, false, 0, null, null, param1Composer, 0, 0, 65534);
return;
}
param1Composer.skipToGroupEnd();
}
}

@Metadata(k = 3, mv = {1, 7, 1}, xi = 48)
static final class CounterKt$Counter$2 extends Lambda implements Function2<Composer, Integer, Unit> {
final int $$changed;

CounterKt$Counter$2(int param1Int) {
super(2);
}

public final void invoke(Composer param1Composer, int param1Int) {
CounterKt.Counter(param1Composer, this.$$changed | 0x1);
}
}
}

这里很乱,我们可以先看一个关键字 System.out.println ,我们打印日志的地方。
第一个关键字的位置

1
2
3
4
5
6
7
8
composer.startReplaceableGroup(-492369756);
ComposerKt.sourceInformation(composer, "C(remember):Composables.kt#9igjgp");
Object object = composer.rememberedValue();
if (object == Composer.Companion.getEmpty()) {
object = SnapshotStateKt.mutableStateOf$default(Integer.valueOf(LiveLiterals$CounterKt.INSTANCE.Int$arg-0$call-mutableStateOf$fun-$anonymous$$arg-0$call-remember$val-count$delegate$fun-$anonymous$$arg-3$call-Column$fun-Counter()), null, 2, null);
composer.updateRememberedValue(object);
}
composer.endReplaceableGroup();

startReplaceableGroup和endReplaceableGroup明显是一对组合,中间的代码貌似是执行了count的remember初始化动作。

第二个print的位置进行了button的onclick匿名内部类的初始化操作,也有一对start和end。
在end之后和最后一个print之前绘制button。

现在能分析出来的东西很少,感觉start和end做了一个标记,remember动作转化成了每次从composer读取内容,内容为空时进行一次初始化操作。

最后的scopeUpdateScope.updateScope执行了compose的刷新操作,这个刷新是怎么做的呢:

1
2
3
4
5
6
7
8
9
10
11
12
@Metadata(k = 3, mv = {1, 7, 1}, xi = 48)
static final class CounterKt$Counter$2 extends Lambda implements Function2<Composer, Integer, Unit> {
final int $$changed;

CounterKt$Counter$2(int param1Int) {
super(2);
}

public final void invoke(Composer param1Composer, int param1Int) {
CounterKt.Counter(param1Composer, this.$$changed | 0x1);
}
}

原来就是重新执行一遍这个compose方法。Count的第二个参数表现奇怪,暂时不知道什么作用。

1
2
3
4
5
6
7
composer.startReusableNode();
if (composer.getInserting()) {
composer.createNode(function0);
} else {
composer.useNode();
}
composer.disableReusing();

node貌似是用来复用的。

接下来我们再看count这个state是怎么触发Text变化的。我们先找到CounterKt$Counter$1$2,Button的匿名内部类。发现

1
TextKt.Text-fLXpl1I(String.valueOf(CounterKt.Counter$lambda-4$lambda-1(this.$count$delegate)), null, 0L, 0L, null, null, null, 0L, null, null, 0L, 0, false, 0, null, null, param1Composer, 0, 0, 65534);

继续

1
2
3
private static final int Counter$lambda-4$lambda-1(MutableState<Integer> paramMutableState) {
return ((Number)((State)paramMutableState).getValue()).intValue();
}

这是什么逻辑呢?转化成这样大家应该能看懂了

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
@Preview
@Composable
fun Counter3() {
Column() {
val count = remember { mutableStateOf(0) }
LaunchedEffect(key1 = Unit) {
delay(5000)
count.value = 10
}
test3 { count.value }
}
}

@Composable
fun test3(provider: () -> Int) {
Text(
text = "${provider.invoke()}"
)
}

这里就能看出by的作用,它能减少state的影响范围。之前刘建分享的例子里state变化引起整个方法重组,这里通过by,把重组控制在了button内部。后面大家写的时候也可以观察下是不是这样。

第二个例子

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
@Preview
@Composable
fun Counter1() {
Column() {
println("1111")
var count by remember { mutableStateOf(0) }
println("2222")
Button(
onClick = {
count++
}
) {
println("3333")
Text(
text = "$count"
)
}
println("4444")

var count2 by remember { mutableStateOf(0) }
println("5555")
Button(
onClick = {
count2++
}
) {
println("6666")
Text(
text = "click"
)
}
println("7777")
Text(
text = "$count2"
)
println("8888")
}
}

count1的变化只会影响第一个button,count2呢,点击时输出的是:

1
2
3
4
5
6
7
I/System.out: 1111
I/System.out: 2222
I/System.out: 3333
I/System.out: 4444
I/System.out: 5555
I/System.out: 7777
I/System.out: 8888

结论是引起整个方法重组,和scopeUpdateScope.updateScope是一致的。
贴一下逆向结果:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
public final class Counter1Kt {
public static final void Counter1(Composer paramComposer, int paramInt) {
Composer composer = paramComposer.startRestartGroup(-717625547);
ComposerKt.sourceInformation(composer, "C(Counter1)17@391L703:Counter1.kt#ptgicz");
if (paramInt != 0 || !composer.getSkipping()) {
composer.startReplaceableGroup(-483455358);
ComposerKt.sourceInformation(composer, "C(Column)P(2,3,1)76@3834L61,77@3900L133:Column.kt#2w3rfo");
Modifier modifier = (Modifier)Modifier.Companion;
MeasurePolicy measurePolicy = ColumnKt.columnMeasurePolicy(Arrangement.INSTANCE.getTop(), Alignment.Companion.getStart(), composer, 0 >> 3 & 0xE | 0 >> 3 & 0x70);
composer.startReplaceableGroup(-1323940314);
ComposerKt.sourceInformation(composer, "C(Layout)P(!1,2)74@2915L7,75@2970L7,76@3029L7,77@3041L460:Layout.kt#80mrfh");
CompositionLocal compositionLocal1 = (CompositionLocal)CompositionLocalsKt.getLocalDensity();
ComposerKt.sourceInformationMarkerStart(composer, 2023513938, "C:CompositionLocal.kt#9igjgp");
Object object1 = composer.consume(compositionLocal1);
ComposerKt.sourceInformationMarkerEnd(composer);
object1 = object1;
CompositionLocal compositionLocal2 = (CompositionLocal)CompositionLocalsKt.getLocalLayoutDirection();
ComposerKt.sourceInformationMarkerStart(composer, 2023513938, "C:CompositionLocal.kt#9igjgp");
Object object2 = composer.consume(compositionLocal2);
ComposerKt.sourceInformationMarkerEnd(composer);
object2 = object2;
CompositionLocal compositionLocal3 = (CompositionLocal)CompositionLocalsKt.getLocalViewConfiguration();
ComposerKt.sourceInformationMarkerStart(composer, 2023513938, "C:CompositionLocal.kt#9igjgp");
Object object3 = composer.consume(compositionLocal3);
ComposerKt.sourceInformationMarkerEnd(composer);
object3 = object3;
Function0 function0 = ComposeUiNode.Companion.getConstructor();
Function3 function3 = LayoutKt.materializerOf(modifier);
int i = (0 << 3 & 0x70) << 9 & 0x1C00 | 0x6;
if (!(composer.getApplier() instanceof androidx.compose.runtime.Applier))
ComposablesKt.invalidApplier();
composer.startReusableNode();
if (composer.getInserting()) {
composer.createNode(function0);
} else {
composer.useNode();
}
composer.disableReusing();
Composer composer1 = Updater.constructor-impl(composer);
Updater.set-impl(composer1, measurePolicy, ComposeUiNode.Companion.getSetMeasurePolicy());
Updater.set-impl(composer1, object1, ComposeUiNode.Companion.getSetDensity());
Updater.set-impl(composer1, object2, ComposeUiNode.Companion.getSetLayoutDirection());
Updater.set-impl(composer1, object3, ComposeUiNode.Companion.getSetViewConfiguration());
composer.enableReusing();
function3.invoke(SkippableUpdater.box-impl(SkippableUpdater.constructor-impl(composer)), composer, Integer.valueOf(i >> 3 & 0x70));
composer.startReplaceableGroup(2058660585);
composer.startReplaceableGroup(-1163856341);
ComposerKt.sourceInformation(composer, "C78@3948L9:Column.kt#2w3rfo");
if ((i >> 9 & 0xE & 0xB) != 2 || !composer.getSkipping()) {
ColumnScope columnScope = (ColumnScope)ColumnScopeInstance.INSTANCE;
composer.startReplaceableGroup(-1147866453);
ComposerKt.sourceInformation(composer, "C19@447L30,22@540L39,21@510L183,33@741L30,36@834L40,35@804L183,46@1020L44:Counter1.kt#ptgicz");
if (((0 >> 6 & 0x70 | 0x6) & 0x51) != 16 || !composer.getSkipping()) {
String str = LiveLiterals$Counter1Kt.INSTANCE.String$arg-0$call-println$fun-$anonymous$$arg-3$call-Column$fun-Counter1();
System.out.println(str);
composer.startReplaceableGroup(-492369756);
ComposerKt.sourceInformation(composer, "C(remember):Composables.kt#9igjgp");
Object object = composer.rememberedValue();
if (object == Composer.Companion.getEmpty()) {
object = SnapshotStateKt.mutableStateOf$default(Integer.valueOf(LiveLiterals$Counter1Kt.INSTANCE.Int$arg-0$call-mutableStateOf$fun-$anonymous$$arg-0$call-remember$val-count$delegate$fun-$anonymous$$arg-3$call-Column$fun-Counter1()), null, 2, null);
composer.updateRememberedValue(object);
}
composer.endReplaceableGroup();
MutableState<Integer> mutableState = (MutableState)object;
object = LiveLiterals$Counter1Kt.INSTANCE.String$arg-0$call-println-1$fun-$anonymous$$arg-3$call-Column$fun-Counter1();
System.out.println(object);
composer.startReplaceableGroup(1157296644);
ComposerKt.sourceInformation(composer, "C(remember)P(1):Composables.kt#9igjgp");
boolean bool = composer.changed(mutableState);
object = composer.rememberedValue();
if (bool || object == Composer.Companion.getEmpty()) {
object = new Counter1Kt$Counter1$1$1$1(mutableState);
composer.updateRememberedValue(object);
}
composer.endReplaceableGroup();
ButtonKt.Button((Function0)object, null, false, null, null, null, null, null, null, (Function3)ComposableLambdaKt.composableLambda(composer, -12111205, true, new Counter1Kt$Counter1$1$2(mutableState)), composer, 805306368, 510);
object = LiveLiterals$Counter1Kt.INSTANCE.String$arg-0$call-println-2$fun-$anonymous$$arg-3$call-Column$fun-Counter1();
System.out.println(object);
composer.startReplaceableGroup(-492369756);
ComposerKt.sourceInformation(composer, "C(remember):Composables.kt#9igjgp");
object = composer.rememberedValue();
if (object == Composer.Companion.getEmpty()) {
object = SnapshotStateKt.mutableStateOf$default(Integer.valueOf(LiveLiterals$Counter1Kt.INSTANCE.Int$arg-0$call-mutableStateOf$fun-$anonymous$$arg-0$call-remember$val-count2$delegate$fun-$anonymous$$arg-3$call-Column$fun-Counter1()), null, 2, null);
composer.updateRememberedValue(object);
}
composer.endReplaceableGroup();
mutableState = (MutableState<Integer>)object;
object = LiveLiterals$Counter1Kt.INSTANCE.String$arg-0$call-println-3$fun-$anonymous$$arg-3$call-Column$fun-Counter1();
System.out.println(object);
composer.startReplaceableGroup(1157296644);
ComposerKt.sourceInformation(composer, "C(remember)P(1):Composables.kt#9igjgp");
bool = composer.changed(mutableState);
object = composer.rememberedValue();
if (bool || object == Composer.Companion.getEmpty()) {
object = new Counter1Kt$Counter1$1$3$1(mutableState);
composer.updateRememberedValue(object);
}
composer.endReplaceableGroup();
ButtonKt.Button((Function0)object, null, false, null, null, null, null, null, null, ComposableSingletons$Counter1Kt.INSTANCE.getLambda-1$app_debug(), composer, 805306368, 510);
object = LiveLiterals$Counter1Kt.INSTANCE.String$arg-0$call-println-4$fun-$anonymous$$arg-3$call-Column$fun-Counter1();
System.out.println(object);
TextKt.Text-fLXpl1I(String.valueOf(Counter1$lambda-8$lambda-5(mutableState)), null, 0L, 0L, null, null, null, 0L, null, null, 0L, 0, false, 0, null, null, composer, 0, 0, 65534);
object = LiveLiterals$Counter1Kt.INSTANCE.String$arg-0$call-println-5$fun-$anonymous$$arg-3$call-Column$fun-Counter1();
System.out.println(object);
} else {
composer.skipToGroupEnd();
}
composer.endReplaceableGroup();
} else {
composer.skipToGroupEnd();
}
composer.endReplaceableGroup();
composer.endReplaceableGroup();
composer.endNode();
composer.endReplaceableGroup();
composer.endReplaceableGroup();
} else {
composer.skipToGroupEnd();
}
ScopeUpdateScope scopeUpdateScope = composer.endRestartGroup();
if (scopeUpdateScope != null)
scopeUpdateScope.updateScope(new Counter1Kt$Counter1$2(paramInt));
}

private static final int Counter1$lambda-8$lambda-1(MutableState<Integer> paramMutableState) {
return ((Number)((State)paramMutableState).getValue()).intValue();
}

private static final void Counter1$lambda-8$lambda-2(MutableState<Integer> paramMutableState, int paramInt) {
paramMutableState.setValue(Integer.valueOf(paramInt));
}

private static final int Counter1$lambda-8$lambda-5(MutableState<Integer> paramMutableState) {
return ((Number)((State)paramMutableState).getValue()).intValue();
}

private static final void Counter1$lambda-8$lambda-6(MutableState<Integer> paramMutableState, int paramInt) {
paramMutableState.setValue(Integer.valueOf(paramInt));
}

@Metadata(k = 3, mv = {1, 7, 1}, xi = 48)
static final class Counter1Kt$Counter1$1$1$1 extends Lambda implements Function0<Unit> {
final MutableState<Integer> $count$delegate;

Counter1Kt$Counter1$1$1$1(MutableState<Integer> param1MutableState) {
super(0);
}

public final void invoke() {
int i = Counter1Kt.Counter1$lambda-8$lambda-1(this.$count$delegate);
Counter1Kt.Counter1$lambda-8$lambda-2(this.$count$delegate, i + 1);
}
}

@Metadata(k = 3, mv = {1, 7, 1}, xi = 48)
static final class Counter1Kt$Counter1$1$2 extends Lambda implements Function3<RowScope, Composer, Integer, Unit> {
final MutableState<Integer> $count$delegate;

Counter1Kt$Counter1$1$2(MutableState<Integer> param1MutableState) {
super(3);
}

public final void invoke(RowScope param1RowScope, Composer param1Composer, int param1Int) {
Intrinsics.checkNotNullParameter(param1RowScope, "$this$Button");
ComposerKt.sourceInformation(param1Composer, "C27@632L51:Counter1.kt#ptgicz");
if ((param1Int & 0x51) != 16 || !param1Composer.getSkipping()) {
String str = LiveLiterals$Counter1Kt.INSTANCE.String$arg-0$call-println$fun-$anonymous$$arg-9$call-Button$fun-$anonymous$$arg-3$call-Column$fun-Counter1();
System.out.println(str);
TextKt.Text-fLXpl1I(String.valueOf(Counter1Kt.Counter1$lambda-8$lambda-1(this.$count$delegate)), null, 0L, 0L, null, null, null, 0L, null, null, 0L, 0, false, 0, null, null, param1Composer, 0, 0, 65534);
return;
}
param1Composer.skipToGroupEnd();
}
}

@Metadata(k = 3, mv = {1, 7, 1}, xi = 48)
static final class Counter1Kt$Counter1$1$3$1 extends Lambda implements Function0<Unit> {
final MutableState<Integer> $count2$delegate;

Counter1Kt$Counter1$1$3$1(MutableState<Integer> param1MutableState) {
super(0);
}

public final void invoke() {
int i = Counter1Kt.Counter1$lambda-8$lambda-5(this.$count2$delegate);
Counter1Kt.Counter1$lambda-8$lambda-6(this.$count2$delegate, i + 1);
}
}

@Metadata(k = 3, mv = {1, 7, 1}, xi = 48)
static final class Counter1Kt$Counter1$2 extends Lambda implements Function2<Composer, Integer, Unit> {
final int $$changed;

Counter1Kt$Counter1$2(int param1Int) {
super(2);
}

public final void invoke(Composer param1Composer, int param1Int) {
Counter1Kt.Counter1(param1Composer, this.$$changed | 0x1);
}
}
}

更加复杂了,而且也看不出来为什么范围不一样。那么上第三个例子。

第三个例子

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
@Preview
@Composable
fun Counter2() {
Column() {
println("1111")
var count by remember { mutableStateOf(0) }
println("2222")
Button(
onClick = {
count++
}
) {
println("3333")
Text(
text = "$count"
)
}
println("4444")
test()
}
}

@Composable
fun test() {
var count2 by remember { mutableStateOf(0) }
println("5555")
Button(
onClick = {
count2++
}
) {
println("6666")
Text(
text = "click"
)
}
println("7777")
Text(
text = "$count2"
)
println("8888")
}

这里单看test的逆向代码

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
public static final void test(Composer paramComposer, int paramInt) {
Composer composer = paramComposer.startRestartGroup(-981726729);
ComposerKt.sourceInformation(composer, "C(test)38@785L30,41@866L32,40@840L147,51@1012L36:Counter2.kt#ptgicz");
if (paramInt != 0 || !composer.getSkipping()) {
composer.startReplaceableGroup(-492369756);
ComposerKt.sourceInformation(composer, "C(remember):Composables.kt#9igjgp");
Object object = composer.rememberedValue();
if (object == Composer.Companion.getEmpty()) {
object = SnapshotStateKt.mutableStateOf$default(Integer.valueOf(LiveLiterals$Counter2Kt.INSTANCE.Int$arg-0$call-mutableStateOf$fun-$anonymous$$arg-0$call-remember$val-count2$delegate$fun-test()), null, 2, null);
composer.updateRememberedValue(object);
}
composer.endReplaceableGroup();
MutableState<Integer> mutableState = (MutableState)object;
object = LiveLiterals$Counter2Kt.INSTANCE.String$arg-0$call-println$fun-test();
System.out.println(object);
composer.startReplaceableGroup(1157296644);
ComposerKt.sourceInformation(composer, "C(remember)P(1):Composables.kt#9igjgp");
boolean bool = composer.changed(mutableState);
object = composer.rememberedValue();
if (bool || object == Composer.Companion.getEmpty()) {
object = new Counter2Kt$test$1$1(mutableState);
composer.updateRememberedValue(object);
}
composer.endReplaceableGroup();
ButtonKt.Button((Function0)object, null, false, null, null, null, null, null, null, ComposableSingletons$Counter2Kt.INSTANCE.getLambda-1$app_debug(), composer, 805306368, 510);
object = LiveLiterals$Counter2Kt.INSTANCE.String$arg-0$call-println-1$fun-test();
System.out.println(object);
TextKt.Text-fLXpl1I(String.valueOf(test$lambda-6(mutableState)), null, 0L, 0L, null, null, null, 0L, null, null, 0L, 0, false, 0, null, null, composer, 0, 0, 65534);
object = LiveLiterals$Counter2Kt.INSTANCE.String$arg-0$call-println-2$fun-test();
System.out.println(object);
} else {
composer.skipToGroupEnd();
}
ScopeUpdateScope scopeUpdateScope = composer.endRestartGroup();
if (scopeUpdateScope != null)
scopeUpdateScope.updateScope(new Counter2Kt$test$2(paramInt));
}

也有scopeUpdateScope.updateScope,执行结果也是只会在compose方法内重组。

几个想法,欢迎反驳

  1. 触发了重组不一定表示所有的组件重新绘制,print不是compose组件所以会打印,compose组件有记忆,value不变的情况下不会重复绘制。参考下方的位置记忆法。
  2. 非compose方法尽量在副作用代码块中执行,避免多次执行造成耗时。
  3. state.value的使用Laziness,减少state作用域。

用到了什么理论

Positional Memoization (位置记忆化)

https://effectiveandroid.substack.com/p/positional-memoization-in-jetpack

这块我的理解还不足,大家可以先看引用的文档尝试理解下,后面继续交流。

引用

https://juejin.cn/post/7103336251645755429
https://juejin.cn/post/6889797083667267598