目录
- Compose的诞生
- Compose好处
- Compose 架构
- @Composable的背后
- 智能重组真的那么智能吗
- 最后
Compose的诞生
在2019年的谷歌IO大会上,Compose作为Android新一代UI开发亮相,因为声明式开发越来越流行了,对标IOS开发SwiftUi,Compose的立项也为Android开发新加了声明式ui的开发选项,在2021年7月1.0正式版本的诞生,也意味着Compose即将进入生产环节,国际app巨头Twitter就首当其冲,在新页面上用上了Compose
Compose好处
与传统的xml相比,Compose不仅吸收了其优点,摒弃了糟粕,还具有以下几个优点
因为Compose是Android团队与JetBrain在推,国内外的学习热情都挺好,目前国内也有不少大厂进行了尝试阶段,比如字节。
Compose 架构
说了这么多,那么Compose是怎么做到在原有View体系做到兼容并改善的呢?
我们从一个例子出发:
class MainActivity : ComponentActivity() { override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) setContent { xxx组件 } } }
我们可以看到,Compose在Activity中,用了setContent方法代替了原有的setContentView方法,那么setContent做了什么呢?
public fun ComponentActivity.setContent( parent: CompositionContext? = null, content: @Composable () -> Unit ) { val existingComposeView = window.decorView .findViewById<ViewGroup>(android.R.id.content) .getChildAt(0) as? ComposeView if (existingComposeView != null) with(existingComposeView) { setParentCompositionContext(parent) setContent(content) } else ComposeView(this).apply { // Set content and parent **before** setContentView // to have ComposeView create the composition on attach setParentCompositionContext(parent) setContent(content) // Set the view tree owners before setting the content view so that the inflation process // and attach listeners will see them already present setOwners() setContentView(this, DefaultActivityContentLayoutParams) } }
可以看到,android.R.id.content的第一个孩子被替换成了ComposeView,而这个ComposeView,就是Compose环境的提供者,在这里面,Compose将剔除原本View体系的测量逻辑,从而采用自己的测量架构。如下图包饺子架构所示:
值得注意的是ComposeView是继承于AbstractComposeView(他定义了compose环境的规范),还有就是android中是多window架构的,所以针对Dialog这种,也有特别的环境实现类。针对PopupWindow这种共用window的组件,也同样提供了compose环境实现类
可以看到架构图中,AbstractComposeView其实也是继承于ViewGroup的。所以准确来说,Compose并没有完全脱离AndroidView体系,而是在这之上建立起了中间层,这就印证了一句老话,没有什么架构是不能解决的,如果有,那就加个中间层!而这个中间层,提供了全新的设计,从而让android得以脱胎换骨到一个新架构!
@Composable的背后
说到Compose,那么肯定离不开Compsoable的介绍,我们肯定会有一个疑问,为什么一个函数加上了Composable注解,就变成了一个可见的视图了呢?比如
@Composable fun Greeting(name: String) { Text(text = "Hello $name!") }
我们进行反编译后
public static final void Greeting(String name, Composer $composer, int $changed) { Intrinsics.checkNotNullParameter(name, "name"); Composer $composer2 = $composer.startRestartGroup(-154424256); ComposerKt.sourceInformation($composer2, "C(Greeting)46@1589L27:MainActivity.kt#8m9ksz"); int $dirty = $changed; if (($changed & 14) == 0) { $dirty |= $composer2.changed(name) ? 4 : 2; } if ((($dirty & 11) ^ 2) != 0 || !$composer2.getSkipping()) { TextKt.m1219TextfLXpl1I(LiveLiterals$MainActivityKt.INSTANCE.m4567String$0$str$arg0$callText$funGreeting() + name + LiveLiterals$MainActivityKt.INSTANCE.m4584String$2$str$arg0$callText$funGreeting(), null, 0L, 0L, null, null, null, 0L, null, null, 0L, 0, false, 0, null, null, $composer2, 0, 0, 65534); } else { $composer2.skipToGroupEnd(); } ScopeUpdateScope endRestartGroup = $composer2.endRestartGroup(); if (endRestartGroup == null) { return; } endRestartGroup.updateScope(new MainActivityKt$Greeting$1(name, $changed)); }
可以看到,实际上,编译器为我们的带有@Composable的Greeting方法添加了两个参数,
Composer $composer, int $changed
Composer就是我们真的compose执行时的操作者,changed就是一个参与是否重组的标识之一,这种通过注解在编译时生成对应参数的方案,在Coroutine里面也用到,当然我们也可以通过ASM等方法去编译时判断注解并更改相应的函数方法,只不过这部分由compose编译器帮我们做了。
我们重点关注一下composer.startRestartGroup这个方法
override fun startRestartGroup(key: Int): Composer { start(key, null, false, null) addRecomposeScope() return this }
可以看到传入了一个key为Int类型的标识,所有Composable函数区域会通过特定key去判断重组的范围以及当前范围是否进行更新(范围就是startRestartGroup - endRestartGroup 之间的状态),当然一个Composable函数里面可能存在多个重组范围,我们还是拿上面的Greeting函数做个例子,不过这次有点变化
@Composable fun Greeting(name: String) { val state by remember { mutableStateOf(true) } if (state){ Text(text = "true") }else{ Text(text = "false") } }
此时反编译后就会多一些代码
public static final void Greeting(String name, Composer $composer, int $changed) { Object value$iv$iv; Intrinsics.checkNotNullParameter(name, "name"); Composer $composer2 = $composer.startRestartGroup(-154424404); ComposerKt.sourceInformation($composer2, "C(Greeting)43@1455L45:MainActivity.kt#8m9ksz"); if (($changed & 1) == 0 && $composer2.getSkipping()) { $composer2.skipToGroupEnd(); } else { $composer2.startReplaceableGroup(-492369756); 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__SnapshotStateKt.mutableStateOf$default(Boolean.valueOf(LiveLiterals$MainActivityKt.INSTANCE.m4554x2b38c863()), null, 2, null); $composer2.updateRememberedValue(value$iv$iv); } else { value$iv$iv = it$iv$iv; } $composer2.endReplaceableGroup(); MutableState state$delegate = (MutableState) value$iv$iv; // 关注这里 if (m4607Greeting$lambda1(state$delegate)) { $composer2.startReplaceableGroup(-154424297); ComposerKt.sourceInformation($composer2, "47@1525L19"); TextKt.m1219TextfLXpl1I(LiveLiterals$MainActivityKt.INSTANCE.m4597String$arg0$callText$branch$if$funGreeting(), null, 0L, 0L, null, null, null, 0L, null, null, 0L, 0, false, 0, null, null, $composer2, 0, 0, 65534); $composer2.endReplaceableGroup(); } else { $composer2.startReplaceableGroup(-154424258); ComposerKt.sourceInformation($composer2, "49@1564L20"); TextKt.m1219TextfLXpl1I(LiveLiterals$MainActivityKt.INSTANCE.m4598String$arg0$callText$else$if$funGreeting(), null, 0L, 0L, null, null, null, 0L, null, null, 0L, 0, false, 0, null, null, $composer2, 0, 0, 65534); $composer2.endReplaceableGroup(); } } ScopeUpdateScope endRestartGroup = $composer2.endRestartGroup(); if (endRestartGroup == null) { return; } endRestartGroup.updateScope(new MainActivityKt$Greeting$1(name, $changed)); }
可以看到,我们状态state变量改变的时候,compose会在if里面生成先startReplaceableGroup,代表着这个范围是可以被替换的,也就是说当if成立的时候会生成一个composition节点,此时如果state变成了false,那么这个if里面的范围group就会被移除,此时父group(即在之前调用startgroup的范围)以替换的方式载入新的else,子group,这就是startReplaceableGroup的含义。
总之,我们可以注意到,key就是一个非常关键的点,就是让我们compose识别到哪些范围能够进行重组,哪些不能!在if else语句中,如果我们依赖一个state,就会在相应的if语句里面插入一个新的子范围group,这就是compose架构中的的智能重组!
智能重组真的那么智能吗
上面我们提到了智能重组的这个概念,那么智能重组真的如字面所说,那么“智能”吗?我们很容易想到,既然compose能在if else这种有作用域的关键字上加上子group,如果是list这种呢?存在着循环语句的条件呢?就算在循环语句里面加上子group,并不能满足我们想要的需求呀!举个