在研究 Jetpack Compose 示例项目时,我看到了@Stable注释@Immutable。我一直在浏览Android 文档和GitHub关于这些注释的信息,但我不明白。
据我了解,如果使用@Immutable,即使状态改变,也不应该发生重组。然而,作为测试的结果,重组仍在继续。
@StableJetpack Compose 中注释的具体作用是什么@Immutable?
我发现可组合屏幕在 Navhost 导航期间多次重新组合,以下示例显示了我如何将导航与日志集成以识别函数被调用的次数,
internal sealed class Screen(val route: String) {
object Splash : Screen("splash")
object Login : Screen("login")
object Credentials : Screen("credentials/{type}") {
fun createRoute(type: String) = "credentials/$type"
}
}
@Composable
fun HostNavGraph(
modifier: Modifier = Modifier,
startDestination: String = Splash.route,
) {
val navController = rememberNavController()
val vm: CredentialsViewModel = getViewModel()
NavHost(navController = navController, startDestination = startDestination, modifier = modifier) {
composable(route = Splash.route) {
Log.e("composable", " Splash")
SplashScreen(openLogin = {
navController.navigate(Login.route)
}, openRegistration = { type ->
navController.navigate(Credentials.createRoute(type)) …Run Code Online (Sandbox Code Playgroud) android android-jetpack-navigation android-jetpack-compose jetpack-compose-navigation compose-recomposition
我很困惑,有人可以向我解释一下两者之间的区别吗:
val variable by remember { mutableStateOf() }
Run Code Online (Sandbox Code Playgroud)
和
val variable by rememberUpdatedState()
Run Code Online (Sandbox Code Playgroud)
当我检查源代码时,rememberUpdatedStates我实际上看到:remember { mutableStateOf() }
@Composable
fun <T> rememberUpdatedState(newValue: T): State<T> = remember {
mutableStateOf(newValue)
}.apply { value = newValue }
Run Code Online (Sandbox Code Playgroud) android kotlin android-jetpack-compose compose-recomposition
Android Studio 大黄蜂补丁 3
从这个页面,它说我可以看到重组计数: https: //developer.android.com/jetpack/compose/tooling#recomposition-counts
它没有向我显示菜单选项。仅设备名称和“停止检查器”
android layout-inspector android-jetpack android-jetpack-compose compose-recomposition
我正在做实验来理解重组和智能重组并制作了一个样本
\n\n抱歉,颜色是用 Random.nextIn() 生成的,以便在视觉上观察重组,设置颜色对重组没有影响,也尝试不更改颜色。
\ngif的内容由三部分组成
\n样品1
\n@Composable\nprivate fun Sample1() {\n\n Column(\n modifier = Modifier\n .background(getRandomColor())\n .fillMaxWidth()\n .padding(4.dp)\n ) {\n var counter by remember { mutableStateOf(0) }\n\n\n Text("Sample1", color = getRandomColor())\n\n Button(\n modifier = Modifier\n .fillMaxWidth()\n .padding(vertical = 4.dp),\n colors = ButtonDefaults.buttonColors(backgroundColor = getRandomColor()),\n onClick = {\n counter++\n }) {\n Text("Counter: $counter", color = getRandomColor())\n }\n }\n}\nRun Code Online (Sandbox Code Playgroud)\n我在这里没有问题,因为智能组合按预期工作,Text最重要的是不读取更改,counter因此重组仅发生在Textinside Button。
样品2
\n@Composable\nprivate fun Sample2() {\n Column(\n modifier …Run Code Online (Sandbox Code Playgroud) android kotlin android-jetpack-compose compose-recomposition
当我删除 mutableStateListOf 中的项目时,我收到 IndexOutOfBoundsException,其中 MyType 是某种自定义类型。myList 中有大约 250 项。这些项目将循环删除。几乎 6 个元素同时在视口中可见。当项目计数达到小于或等于 6(滚动位置位于顶部)时,会引发此异常并且应用程序崩溃。
此外,仅当 Listview 为活动屏幕时应用程序才会崩溃。否则它不会崩溃/不会引发此索引出界异常。我已经调试了应用程序逻辑,并确保进行检查以忽略索引大于列表大小的任何更新/添加/删除请求。我怀疑仅在重组期间引发异常。我观察到的另一个行为是,当列表滚动到底部并且项目被删除时,则在循环的最开始就已经引发了此异常。请查看堆栈跟踪。
FATAL EXCEPTION: main Process: com.XXX.YYY.ZZZ, PID: 16225 java.lang.IndexOutOfBoundsException: Index 6, size 6 at androidx.compose.foundation.lazy.layout.MutableIntervalList.checkIndexBounds(IntervalList.kt:177) at androidx.compose.foundation.lazy.layout.MutableIntervalList.get(IntervalList.kt:160) at androidx.compose.foundation.lazy.layout.DefaultLazyLayoutItemsProvider.getKey(LazyLayoutItemProvider.kt:232) at androidx.compose.foundation.lazy.LazyListItemProviderImpl.getKey(Unknown Source:2) at androidx.compose.foundation.lazy.layout.DefaultDelegatingLazyLayoutItemProvider.getKey(LazyLayoutItemProvider.kt:201) at androidx.compose.foundation.lazy.LazyListItemProviderKt$rememberLazyListItemProvider$1$1.getKey(Unknown Source:2) at androidx.compose.foundation.lazy.LazyMeasuredItemProvider.getAndMeasure-ZjPyQlc(LazyMeasuredItemProvider.kt:46) at androidx.compose.foundation.lazy.LazyListMeasureKt.measureLazyList-Hh3qtAg(LazyListMeasure.kt:164) at androidx.compose.foundation.lazy.LazyListKt$rememberLazyListMeasurePolicy$1$1.invoke-0kLqBqw(LazyList.kt:299) at androidx.compose.foundation.lazy.LazyListKt$rememberLazyListMeasurePolicy$1$1.invoke(LazyList.kt:190) at androidx.compose.foundation.lazy.layout.LazyLayoutKt$LazyLayout$1$2$1.invoke-0kLqBqw(LazyLayout.kt:71) at androidx.compose.foundation.lazy.layout.LazyLayoutKt$LazyLayout$1$2$1.invoke(LazyLayout.kt:69) at androidx.compose.ui.layout.LayoutNodeSubcompositionsState$createMeasurePolicy$1.measure-3p2s80s(SubcomposeLayout.kt:598) at androidx.compose.ui.node.InnerNodeCoordinator.measure-BRTryo0(InnerNodeCoordinator.kt:103) at androidx.compose.ui.graphics.SimpleGraphicsLayerModifier.measure-3p2s80s(GraphicsLayerModifier.kt:635) at androidx.compose.ui.node.LayoutModifierNodeCoordinator.measure-BRTryo0(LayoutModifierNodeCoordinator.kt:155) at androidx.compose.foundation.layout.SizeModifier.measure-3p2s80s(Size.kt:781) at androidx.compose.ui.node.BackwardsCompatNode.measure-3p2s80s(BackwardsCompatNode.kt:323) at androidx.compose.ui.node.LayoutModifierNodeCoordinator.measure-BRTryo0(LayoutModifierNodeCoordinator.kt:155) at androidx.compose.ui.node.LayoutNodeLayoutDelegate$performMeasure$2.invoke(LayoutNodeLayoutDelegate.kt:1090) at androidx.compose.ui.node.LayoutNodeLayoutDelegate$performMeasure$2.invoke(LayoutNodeLayoutDelegate.kt:1086) at androidx.compose.runtime.snapshots.Snapshot$Companion.observe(Snapshot.kt:2200) at androidx.compose.runtime.snapshots.SnapshotStateObserver$observeReads$1$1.invoke(SnapshotStateObserver.kt:234) at androidx.compose.runtime.snapshots.SnapshotStateObserver$observeReads$1$1.invoke(SnapshotStateObserver.kt:230) at androidx.compose.runtime.SnapshotStateKt__DerivedStateKt.observeDerivedStateRecalculations(DerivedState.kt:341) at …
indexoutofboundsexception compose-recomposition android-jetpack-compose-lazy-column
@Composable 函数被重构
当items: List<Int>作为参数传递时,compose 总是重新组合,无论是否List是不可变的且无法更改。(List是没有@Stable注解的接口)。因此,任何接受List<T>参数的可组合函数总是会被重组,而不是智能重组。
如何标记List<T>为稳定,以便编译器知道 List 是不可变的并且函数永远不需要因此而重新组合?
我发现的唯一方法就是像这样包裹@Immutable data class ImmutableList<T>(val items: List<T>)。演示(当 Child1 重组 Parent 时,具有相同 List 的 Child2 也会被重组):
class TestActivity : AppCompatActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContent {
ComposeBasicsTheme {
Parent()
}
}
}
}
@Composable
fun Parent() {
Log.d("Test", "Parent Draw")
val state = remember { mutableStateOf(false) }
val items = remember { listOf(1, 2, 3) …Run Code Online (Sandbox Code Playgroud) 在 Navhost 导航期间,我发现可组合屏幕多次重新组合。因此,我的 ViewModel 也多次调用 API 数据源。
@Composable
fun MainView() {
val scaffoldState = rememberScaffoldState(rememberDrawerState(DrawerValue.Closed))
val scope = rememberCoroutineScope()
val navController = rememberNavController()
Scaffold(
scaffoldState = scaffoldState,
topBar = { TopBar(
toolbarTitle = stringResource(id = R.string.app_name),
scope = scope,
scaffoldState = scaffoldState
) },
drawerContent = {
DrawerView(scope = scope, scaffoldState = scaffoldState, navController = navController)
},
) {
NavGraph(navController = navController)
}
}
@Composable
fun NavGraph(navController: NavHostController) {
NavHost(navController, startDestination = NavDrawerItem.Repositories.route) {
composable(NavDrawerItem.Repositories.route) {
RepoListView(getViewModel())
}
composable(NavDrawerItem.EmojiList.route) { …Run Code Online (Sandbox Code Playgroud) android android-jetpack-navigation android-jetpack-compose jetpack-compose-navigation compose-recomposition
我正在尝试执行一些列表操作,但遇到了单个项目更新时所有项目重新组合的问题。
我的模特;
data class Person(val id: Int, val name: String, val isSelected: Boolean = false)
@Stable
data class PersonsWrapper(val persons: List<Person>)
Run Code Online (Sandbox Code Playgroud)
我的ViewModel和更新功能;
private val initialList = listOf(
Person(id = 0, name = "Name0"),
Person(id = 1, name = "Name1"),
Person(id = 2, name = "Name2"),
Person(id = 3, name = "Name3"),
Person(id = 4, name = "Name4"),
Person(id = 5, name = "Name5"),
Person(id = 6, name = "Name6"),
)
val list = mutableStateOf(PersonsWrapper(initialList))
fun updateItemSelection(id: Int) {
val …Run Code Online (Sandbox Code Playgroud) android android-jetpack-compose compose-recomposition android-jetpack-compose-lazy-column
我有一个 MVI 架构,其中包含主屏幕的活动
class MainActivity : ComponentActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContent {
MyTheme {
val viewModel: MainScreenViewModel = viewModel(factory = MainScreenViewModel.Factory)
val state by viewModel.viewState.collectAsState()
MainScreen(state, viewModel::handleEvent)
}
}
}
}
Run Code Online (Sandbox Code Playgroud)
主屏幕看起来有点像这样:
@Composable
fun MainScreen(state: MainScreenState, onEvent: (Event) -> Unit) {
TaskList(
tasks = state.items,
onEvent = onEvent,
)
}
@Composable
fun TaskList(tasks: List<TaskState>, onEvent: (Event) -> Unit) {
LazyColumn {
items(items = tasks, key = { it.id }) {
Task(state = it, onEvent = onEvent) …Run Code Online (Sandbox Code Playgroud) android kotlin android-jetpack-compose compose-recomposition