保存重组时的状态,并可以有重组后取出之前的状态

引用官方的栗子:

@Composable fun ContactList( contacts: List, comparator: Comparator, modifier: Modifier = Modifier ) { LazyColumn(modifier) { // DON’T DO THIS items(contacts.sortedWith(comparator)) { contact -> // … } } }

LazyColumn在滑动时,会使自身状态发生改变导致ContactList重组,从而contacts.sortedWith(comparator)也会重复执行。而排序是一个占用CPU算力的函数,对性能产生了较大的影响。

正确做法:

@Composable fun ContactList( contacts: List, comparator: Comparator, modifier: Modifier = Modifier ) { val sortedContacts = remember(contacts, sortComparator) { contacts.sortedWith(sortComparator) }

LazyColumn(modifier) { items(sortedContacts) { // … } } }

使用remember会对排序的结果进行保存,使得下次重组时,只要contacts不发生变化 ,其值可以重复使用。也就是说,它只进行了一次排序操作,避免了每次重组时都进行了计算。

提示:

更优的做法是将这类计算的操作移出Compose方法,放到ViewModel中,再使用collectAsState或LanchEffect等方式进行观测自动重组。

2、使用LazyColumn、LazyRow等列表组件时,指定key

如下一段代码,是一个很常见的需求(from官网):

NoteRow记录每项记录的简要信息,当我们进入编辑页进行修改后,需要将最近修改的一条按修改时间放到列表最前面。这时,假若不指定每项Item的Key,其中一项发生了位置变化,都会导致其他的NoteRow发生重组,然而我们修改的只是其中一项,进行了不必要的渲染。

@Composable fun NotesList(notes: List) { LazyColumn { items( items = notes ) { note -> NoteRow(note) } } }

正确的做法:

为每项Item提供 项键,就可避免其他未修改的NoteRow只需挪动位置,避免发生重组

@Composable fun NotesList(notes: List) { LazyColumn { items( items = notes, key = { note -> // 为每项Item提供稳定的、不会发生改变的唯一值(通常为项ID) note.id } ) { note -> NoteRow(note) } } }

3、使用 derivedStateOf 限制重组

假设我们需要根据列表的第一项是否可见来决定划到顶部的按钮是否可见,代码如下:

val listState = rememberLazyListState()

LazyColumn(state = listState) { // … }

val showButton = listState.firstVisibleItemIndex > 0

AnimatedVisibility(visible = showButton) { ScrollToTopButton() }

由于列表的滑动会使listState状态改变,而使用showButton的AnimatedVisibility会不断重组,导致性能下降。

解决方案是使用派生状态。如下 :

val listState = rememberLazyListState()

LazyColumn(state = listState) { // … }

val showButton by remember { derivedStateOf { listState.firstVisibleItemIndex > 0 } }

AnimatedVisibility(visible = showButton) { ScrollToTopButton() }

派生状态,可以这样理解,只有在derivedStateOf里的状态发生改变时,只关注和派发对UI界面产生了影响的状态。这样AnimatedVisibility只会在改变时发生重组。对应的应用场景是,状态发生了改变,但是我们只关注对界面产生了影响的状态进行分发,这种情况下,就可以考虑使用。

4、尽可能延迟State的读行为

之前我们提到,对于一个Compose页面来说,它会经历以下步骤:

第一步,Composition,这其实就代表了我们的Composable函数执行的过程。第二步,Layout,这跟我们View体系的Layout类似,但总体的分发流程是存在一些差异的。第三步,Draw,也就是绘制,Compose的UI元素最终会绘制在Android的Canvas上。由此可见,Jetpack Compose虽然是全新的UI框架,但它的底层并没有脱离Android的范畴。最后,Recomposition,也就是重组,并且重复1、2、3步骤。

尽可能推迟状态读取的原因,其实还是希望我们可以在某些场景下直接跳过Recomposition的阶段、甚至Layout的阶段,只影响到Draw。

分析如下代码:

@Composable fun SnackDetail() { // Recomposition Scope // … Box(Modifier.fillMaxSize()) { Start val scroll = rememberScrollState(0) // … Title(snack, scroll.value) // 1,状态读取 // … } // Recomposition Scope End }

@Composable private fun Title(snack: Snack, scroll: Int) { // … val offset = with(LocalDensity.current) { scroll.toDp() } Column( modifier = Modifier .offset(y = offset) // 2,状态使用 ) { // … } }

上面的代码有两个注释,注释1,代表了状态的读取;注释2,代表了状态的使用。这种“状态读取与使用位置不一致”的现象,其实就为Compose提供了性能优化的空间。

那么,具体我们该如何优化呢?简单来说,就是让:“状态读取与使用位置一致”。

改为如下 :

// 代码段12

@Composable fun SnackDetail() { // Recomposition Scope // …

Box(Modifier.fillMaxSize()) { val scroll = rememberScrollState(0) // … Title(snack) { scroll.value } // 1,Laziness // … } // Recomposition Scope End }

@Composable private fun Title(snack: Snack, scrollProvider: () -> Int) { // … val offset = with(LocalDensity.current) { scrollProvider().toDp() } Column( modifier = Modifier .offset(y = offset) // 2,状态读取+使用 ) { // …

自我介绍一下,小编13年上海交大毕业,曾经在小公司待过,也去过华为、OPPO等大厂,18年进入阿里一直到现在。

深知大多数HarmonyOS鸿蒙开发工程师,想要提升技能,往往是自己摸索成长或者是报班学习,但对于培训机构动则几千的学费,着实压力不小。自己不成体系的自学效果低效又漫长,而且极易碰到天花板技术停滞不前!

因此收集整理了一份《2024年HarmonyOS鸿蒙开发全套学习资料》,初衷也很简单,就是希望能够帮助到想自学提升又不知道该从何学起的朋友,同时减轻大家的负担。

既有适合小白学习的零基础资料,也有适合3年以上经验的小伙伴深入学习提升的进阶课程,基本涵盖了95%以上HarmonyOS鸿蒙开发知识点,真正体系化!

由于文件比较大,这里只是将部分目录大纲截图出来,每个节点里面都包含大厂面经、学习笔记、源码讲义、实战项目、讲解视频,并且会持续更新

如果你觉得这些内容对你有帮助,可以添加VX:vip204888 (备注鸿蒙获取)

一个人可以走的很快,但一群人才能走的更远。不论你是正从事IT行业的老鸟或是对IT行业感兴趣的新人,都欢迎扫码加入我们的的圈子(技术交流、学习资源、职场吐槽、大厂内推、面试辅导),让我们一起学习成长!

文件比较大,这里只是将部分目录大纲截图出来,每个节点里面都包含大厂面经、学习笔记、源码讲义、实战项目、讲解视频,并且会持续更新**

如果你觉得这些内容对你有帮助,可以添加VX:vip204888 (备注鸿蒙获取) [外链图片转存中…(img-PIfLcan4-1712642778241)]

一个人可以走的很快,但一群人才能走的更远。不论你是正从事IT行业的老鸟或是对IT行业感兴趣的新人,都欢迎扫码加入我们的的圈子(技术交流、学习资源、职场吐槽、大厂内推、面试辅导),让我们一起学习成长!

推荐文章

评论可见,请评论后查看内容,谢谢!!!
 您阅读本篇文章共花了: