缘起
真正使用Compose做线上项目还是在两年前了,详见这篇文章《直播、聊天交友APP的开发及上架GooglePlay总结【Compose版】》,文章地址在:https://juejin.cn/post/7042181171706331150。去年由于职位的变动移交给了面向海外的团队人员开发,后来虽然没有专门做Compose的项目了,但是自己写Android端示例项目或者桌面端项目的时候都会第一选择Compose来进行开发。
最近海外组的小伙伴做复盘的时候发现一件离奇的事情,Compose的“重组”在有些情况下没有按照预想的来,是我们预想不对呢?还是出现了其它隐形的影响重组的因素呢?官方说的所有函数类型(lambda)是稳定的类型到底靠不靠谱呢?
注: 该文章基于Compose 1.3.0版本编写,其它版本暂未进行实验。
场景复现
首先我们要把遇到的问题重新复现出来,这种情况也是费了我九牛二虎之力,由于思维的惯性以及多年没有继续深耕Compose,说多了都是泪。
主要的UI效果很简单,第一层是一个Text和一个Box组件,Text组件中的文本数量跟随下层Button组件的点击次数不断增加,Box组件也添加了点击事件,点击也可使得数字增加。
场景类Activity如下所示,已将部分代码精简处理,注意其中的mTemp变量,虽然全局都没有使用它。我们主要需要关注的是 WrapperBox() 函数,它包含了一个Modifier参数和函数类型的参数,按官方的说法来说应该是不会重组的:
class SceneActivity : ComponentActivity() {
private val mCurrentNum = mutableStateOf(0)
// 这个注释打开、关闭会影响WrapperBox进行重组
// private var mTemp = "Hello"
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContent {
Column {
Row {
Text(
text = "当前数量:${
mCurrentNum.value}",
modifier = Modifier
.fillMaxWidth()
.height(26.dp)
.weight(1f)
.colorBg()
)
WrapperBox(
modifier = Modifier
.fillMaxWidth()
.height(26.dp)
.weight(1f),
onClick = {
mCurrentNum.value++
})
}
Button(onClick = {
mCurrentNum.value++ }) {
Text(text = "点击增加数量")
}
}
}
}
@Composable
private fun WrapperBox(
modifier: Modifier,
onClick: () -> Unit
) {
Box(
modifier = modifier
.clickable {
onClick.invoke()
}
.colorBg()
)
}
}
// 扩展的随机背景色修饰符,每次重组都会显示不同颜色
fun Modifier.colorBg() = this
.background(
color = randomComposeColor(),
shape = RoundedCornerShape(4.dp)
)
.padding(4.dp)
直接给大家看下不同场景下的效果:
- 没有mTemp变量的时候
可以看到,点击按钮的时候,只有左侧的文本组件在重组,文本在跟随点击的数量不断更新,这个情况跟我们所认为的情况是一样的
- 有mTemp变量的时候
这个时候除了左侧的文本组件在不断重组,右侧的Box组件居然也在不断重组(变换颜色)。
为什么多了一个变量就会导致原本不会重组的组件发生重组呢?我们分别看下反编译后的源码,已做部分删减处理:
- 没有mTemp变量的时候
public final class SceneActivity extends ComponentActivity {
public static final int $stable = 0;
public void onCreate(Bundle bundle) {
super.onCreate(bundle);
// ...省略代码
Modifier weight$default = RowScope.weight$default(rowScopeInstance, SizeKt.m473height3ABfNKs(SizeKt.fillMaxWidth$default(Modifier.Companion, 0.0f, 1, null), Dp.m4662constructorimpl(f2)), 1.0f, false, 2, null);
composer.startReplaceableGroup(1157296644);
ComposerKt.sourceInformation(composer, "C(remember)P(1):Composables.kt#9igjgp");
boolean changed = composer.changed(sceneActivity);
Object rememberedValue = composer.rememberedValue();
if (changed || rememberedValue == Composer.Companion.getEmpty()) {
rememberedValue = (Function0) new Function0