compose中的@NonRestartableComposeable
被@NonRestartableComposeable标记的函数
- parent重组时不管该函数参数是否变化,它总是被执行,没有检测参数是否变化的步骤
- 编译器不会给该函数生成重启相关代码代码,该函数只能通过parent的重组来导致该函数的重启
不检测参数变化的例子
1. parent重组,参数不变,该函数不跳过重组
@Composable
fun NonRestartableDemo1() {
var count by remember {
mutableIntStateOf(0)
}
// 1.
println("parent run! count:$count")
Button(onClick = { count++ }) { Text("button") }
// 每次都传固定的"good!"
NonRChild("good!")
}
@Composable
@NonRestartableComposable
fun NonRChild(name: String) {
// 2.
println("child run!")
Text("non r chile")
}
// 点击Button以后输出
// parent run! count:1
// child run!
- 点击Button触发可组合项NonRestartableDemo1的重组,对应函数体再次执行;所以(1.)处println()执行。
- 接着调用(这么说准确吗?不知道反编译代码漏掉了多少编译器处理以后的细节)NonRChild("good!"),由于它被NonRestartableComposable标记,它的重组不跳过。所以(2.)处println()执行。
2. parent重组,参数变化,该函数不跳过重组
@Composable
fun NonRestartableDemo2() {
var count by remember {
mutableIntStateOf(0)
}
// 1.
println("parent run! count:$count")
Button(onClick = { count++ }) { Text("button") }
// 每次传递不同参数
NonRChild2(System.currentTimeMillis().toString())
}
@Composable
@NonRestartableComposable
fun NonRChild2(name: String) {
// 2.
println("child run!")
Text("non r chile")
}
// 点击Button以后输出
// parent run! count:1
// child run!
parent重组了,所以(1.)处println()执行,由于可组合函数NonRChild被NonRestartableComposable标记,所以它对应的重组不被跳过,所以(2.)处println()执行
不生成重启相关代码的说明
@Composable
@NonRestartableComposable
fun NonRestartableDemo3(value:String){
Text("$value")
}
反编译代码如下
public final class NonRestartableDemo3Kt {
@Composable
public static final void NonRestartableDemo3(@NotNull String value, @Nullable Composer $composer, int $changed) {
// ..
$composer.startReplaceGroup(1376374039);
ComposerKt.sourceInformation($composer, "C(NonRestartableDemo3)10@299L14:NonRestartableDemo3.kt#ili49k");
// ..
TextKt.Text--4IGK_g(//..);
// ..
$composer.endReplaceGroup();
}
}
如果去掉@NonRestartableComposable注解,反编译如下
public final class NonRestartableDemo3Kt {
@Composable
public static final void NonRestartableDemo3(@NotNull String value, @Nullable Composer $composer, int $changed) {
// ..
$composer = $composer.startRestartGroup(1376374039);
ComposerKt.sourceInformation($composer, "C(NonRestartableDemo3)10@301L14:NonRestartableDemo3.kt#ili49k");
int $dirty = $changed;
if (($changed & 14) == 0) {
$dirty |= $composer.changed(value) ? 4 : 2;
}
if (($dirty & 11) == 2 && $composer.getSkipping()) {
$composer.skipToGroupEnd();
} else {
//..
TextKt.Text--4IGK_g(//..);
//..
}
// 这里就是重启相关代码;
// 当 NonRestartableDemo3的重启作用域重启时,会使用相同参数再次调用NonRestartableDemo3()方法
// 注意!反编译以后有些细节对不上:
// 1.updateScope()方法参数列表和NonRestartableDemo3Kt::NonRestartableDemo3$lambda$0对不上
// 2.编译器会将NonRestartableDemo3函数体和传递给它的参数包裹到匿名函数中,这里也没体现出来
// 3.等等
ScopeUpdateScope var4 = $composer.endRestartGroup();
if (var4 != null) {
var4.updateScope(NonRestartableDemo3Kt::NonRestartableDemo3$lambda$0);
}
}
private static final Unit NonRestartableDemo3$lambda$0(String $value, int $$changed, Composer $composer, int $force) {
Intrinsics.checkNotNullParameter($value, "$value");
NonRestartableDemo3($value, $composer, RecomposeScopeImplKt.updateChangedFlags($$changed | 1));
return Unit.INSTANCE;
}
}
对比上述反编译后到代码可以验证,加上@NonRestartableComposeable注解以后,编译器不会在对应可组合函数内部生成重启作用域相关代码。
读取State,导致parent重组
var count by mutableIntStateOf(0)
@Composable
fun NonRParent2() {
println("parent2")
NonRParent1()
}
@Composable
fun NonRParent1() {
println("parent1")
NonRChild3()
Button(onClick = { count += 1 }) {
Text("button")
}
}
@Composable
@NonRestartableComposable
fun NonRChild3() {
println("NonRChild3")
NonRChild4()
}
@Composable
@NonRestartableComposable
fun NonRChild4() {
println("NonRChild3:count:$count")
}
// 点击Button以后输出:
// parent1
// NonRChild3
// NonRChild3:count:1
- NonRChild4中读取了State count,count需要关联到重启作用域但是NonRChild4没有对应的重启作用域
- 于是向上查找,查看NonRChild3,发现也没有
- 继续向上查找找到NonRParent1有重启作用域,将count关联到这个重启作用域
- 点击Button以后修改count到值,NonRParent1的作用域与count关联,导致这个重启作用域重启,于是可组合函数NonRChild3被执行
- 由于NonRChild3/NonRChild4被NonRestartableComposable注解了,所以它俩总是执行