diff --git a/compose/src/main/java/org/zhiwei/compose/screen/state/Recomposable.kt b/compose/src/main/java/org/zhiwei/compose/screen/state/Recomposable.kt index f8bdff9..a2eb2d4 100644 --- a/compose/src/main/java/org/zhiwei/compose/screen/state/Recomposable.kt +++ b/compose/src/main/java/org/zhiwei/compose/screen/state/Recomposable.kt @@ -8,6 +8,9 @@ import androidx.compose.foundation.verticalScroll import androidx.compose.material3.Button import androidx.compose.material3.Text import androidx.compose.runtime.Composable +import androidx.compose.runtime.DisposableEffect +import androidx.compose.runtime.LaunchedEffect +import androidx.compose.runtime.SideEffect import androidx.compose.runtime.getValue import androidx.compose.runtime.mutableIntStateOf import androidx.compose.runtime.mutableStateOf @@ -167,12 +170,15 @@ private data class EStr(val str: String) * ⚠️这里特别说明,非内联,因为常用的Column,Row,Box 容器 是内联的inline composable函数。 * 2、在composable作用域内,只要有状态数据发生变化,就会触发reComposable重组。 * 3、重组会尽量 最小化 触发范围。封装出去的composable函数,即使在一个感受变化的作用域内,如果它不接收变化数据,则 其自身也不会重组。 + * 4、初步可以简单理解LaunchEffect、SideEffect、DisposableEffect的感知compose的生命周期的效果 */ @Composable private fun UI_ReComposable() { Title_Text(title = "Recompose重组") Title_Sub_Text(title = "composable元素生命周期相比Android的activity/fragment简单许多,创建--组合(单/多次)--销毁。而重组的多次绘制也不会影响过多的性能。reCompose会最小化组合元素区域,感知数据变化来触发。") RC_Simple() + //重组 的作用域 + UI_CommonStable() } //简单演示 重组 作用域 @@ -208,6 +214,23 @@ private fun RC_Simple() { // 而且,⚠️可以注意,log输出不只是Column的进入,而是会有👀开始的那个log,就因为Column是内联,而非独立composable函数 Text(text = "外部的统计数:${counter.intValue}") } + //composable的控件 三个生命周期:创建--绘制(单/多次)--销毁。不像Activity/Fragment有生命周期回调函数。这里可以用后续会学到的Effect效应函数来监控生命周期 + LaunchedEffect(key1 = null) { + //启动效应函数,会在所属composable作用域进行创建的时候,调用且仅调用一次。内部有协程作用域,会伴随所属compose。 + println("🚀LaunchEffect创建compose的协程") + } + SideEffect { + //SideEffect 会在compose每次重组都调用 + println("♻️每次都会调用。。。") + } + DisposableEffect(key1 = null) { + //销毁compose会调用的效应effect,其内部必须调用onDispose来释放必要的资源 + println("🗑️这里如同LaunchEffect一样,初始化调用一次。") + onDispose { + //这里是compose销毁的时候,调用的作用域。 + println("💨释放.....资源") + } + } } diff --git a/compose/src/main/java/org/zhiwei/compose/screen/state/StabilityImmutable.kt b/compose/src/main/java/org/zhiwei/compose/screen/state/StabilityImmutable.kt new file mode 100644 index 0000000..3f6aa02 --- /dev/null +++ b/compose/src/main/java/org/zhiwei/compose/screen/state/StabilityImmutable.kt @@ -0,0 +1,198 @@ +package org.zhiwei.compose.screen.state + +import androidx.compose.foundation.background +import androidx.compose.foundation.layout.Column +import androidx.compose.foundation.layout.Spacer +import androidx.compose.foundation.layout.fillMaxWidth +import androidx.compose.foundation.layout.height +import androidx.compose.foundation.layout.padding +import androidx.compose.foundation.shape.CutCornerShape +import androidx.compose.material3.Button +import androidx.compose.material3.Text +import androidx.compose.runtime.Composable +import androidx.compose.runtime.SideEffect +import androidx.compose.runtime.Stable +import androidx.compose.runtime.getValue +import androidx.compose.runtime.mutableIntStateOf +import androidx.compose.runtime.mutableStateOf +import androidx.compose.runtime.remember +import androidx.compose.runtime.setValue +import androidx.compose.ui.Modifier +import androidx.compose.ui.draw.shadow +import androidx.compose.ui.graphics.Color +import androidx.compose.ui.unit.dp +import kotlin.random.Random + +/** + * 演示compose中感知数据变化,引起重组; + * 在一些封装的compose独立函数中,接收变化参数与否,会重组与否。 + * 添加@stable,@immutable @NonRestartableComposable 等不同的注解,会有不同的作用效果。 + * @stable 注解可用于类/接口,函数,会标注稳定的。 + * @immutable,标注立即变化的。 + */ +@Composable +internal fun UI_CommonStable() { + //三种不同的数据形式 + var counter by remember { mutableIntStateOf(0) } + val unstableData by remember { mutableStateOf(UnStableDataClass(0)) } + val unstableData2 by remember { mutableStateOf(UnStableDataClass2(0)) } + val stableData by remember { mutableStateOf(StableDataClas(0)) } + + Column { + Button( + onClick = { counter++ }, + Modifier + .fillMaxWidth() + .padding(horizontal = 8.dp) + ) { + Text(text = "当前数:$counter") + } + Spacer(modifier = Modifier.height(20.dp)) + //可观察控制台输出的log看整个重组作用域,也可以看UI运行效果。 + OuterComposable { + println("🔥创建OuterComposable ") + MiddleComposable { + // 本区域内有counter变化,所以会引起该区域重组 + println("🔥创建MiddleComposable ") + //封装的compose独立函数,即使它们接收的参数没有变化,但是由于所在作用域重组,会引起其自身也重组。 + UnstableComposable(data = unstableData) + //这个和上面基本一样,但是data是使用了@stable注解的,所以它不会因为外部区域的重组而重组,除非接收参数真的变化了。 + UnstableComposable2(data = unstableData2) + //然而这个compose函数,接收到的参数是val不可变的,且其是非inline的独立compose组件,所以它不会重组。 + StableComposable(data = stableData) + Counter(text = "计数 $counter") + } + } + } +} + + +//含有一个var可变参数的类 +data class UnStableDataClass(var value: Int) + +//同上,但是添加有@stable注解 +@Stable +data class UnStableDataClass2(var value: Int) + +//含有val不可变参数的类 +data class StableDataClas(val value: Int) + +@Composable +private fun UnstableComposable(data: UnStableDataClass) { + SideEffect { + println("🍎 UnstableComposable") + } + Column( + modifier = Modifier + .padding(4.dp) + .shadow(1.dp, shape = CutCornerShape(topEnd = 8.dp)) + .background(randomColor()) + .fillMaxWidth() + .padding(4.dp) + ) { + //而这个data的value是var可变的,外部触发函数作用域的时候,就引起自身也重组。 + Text(text = "UnstableComposable() value: ${data.value}") + } +} + +//和UnstableComposable基本一致,就是使用的data是不同的类,添加了@stable的注解与否 +@Composable +private fun UnstableComposable2(data: UnStableDataClass2) { + SideEffect { + println("🍎 UnstableComposable2") + } + Column( + modifier = Modifier + .padding(4.dp) + .shadow(1.dp, shape = CutCornerShape(topEnd = 8.dp)) + .background(randomColor()) + .fillMaxWidth() + .padding(4.dp) + ) { + //而这个data的value是var可变的,外部触发函数作用域的时候,就引起自身也重组。 + Text(text = "UnstableComposable() value: ${data.value}") + } +} + + +@Composable +private fun StableComposable(data: StableDataClas) { + SideEffect { + println("🍏 StableComposable") + } + Column( + modifier = Modifier + .padding(4.dp) + .shadow(1.dp, shape = CutCornerShape(topEnd = 8.dp)) + .background(randomColor()) + .fillMaxWidth() + .padding(4.dp) + ) { + //因为这个data的value是val的,在函数内就是不会变化的。 + Text(text = "StableComposable value(): ${data.value}") + } +} + + +/** + * 外框 + */ +@Composable +private fun OuterComposable(content: @Composable () -> Unit) { + //创建compose的时候会调用 + SideEffect { + println("== 外边框的Composable函数 ==") + } + + Column( + modifier = Modifier + .padding(4.dp) + .shadow(1.dp, shape = CutCornerShape(topEnd = 8.dp)) + .background(randomColor()) + .fillMaxWidth() + .padding(4.dp) + ) { content() } +} + +//中间框 +@Composable +private fun MiddleComposable(content: @Composable () -> Unit) { + SideEffect { + println("--- 中间composable ---") + } + Column( + modifier = Modifier + .padding(4.dp) + .shadow(1.dp, shape = CutCornerShape(topEnd = 8.dp)) + .background(randomColor()) + .fillMaxWidth() + .padding(4.dp) + ) { content() } +} + +//计数显示 +@Composable +private fun Counter(text: String) { + SideEffect { + println("--->>> 🧮🔢$text🧄⬆️ <<<---") + } + Column( + modifier = Modifier + .padding(4.dp) + .shadow(1.dp, shape = CutCornerShape(topEnd = 8.dp)) + .background(randomColor()) + .fillMaxWidth() + .padding(4.dp) + ) { + Text(text = "计数框: $text") + } +} + +private fun randomColor(): Color { + return Color( + Random.nextInt(255), + Random.nextInt(255), + Random.nextInt(255), + Random.nextInt(255), + ) +} \ No newline at end of file