Android Jetpack Compose使用及性能优化小结_composer 优化

先自我介绍一下,小编浙江大学毕业,去过华为、字节跳动等大厂,目前阿里P7

深知大多数程序员,想要提升技能,往往是自己摸索成长,但自己不成体系的自学效果低效又漫长,而且极易碰到天花板技术停滞不前!

因此收集整理了一份《2024年最新Android移动开发全套学习资料》,初衷也很简单,就是希望能够帮助到想自学提升又不知道该从何学起的朋友。
img
img
外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传
img
外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传
外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传
img

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

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

如果你需要这些资料,可以添加V获取:vip204888 (备注Android)
外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

正文

在一次项目开发中接触到了jetpack Compose,并且还项目中在逻辑简单的页面,使用了compose去实现。当时觉得很新颖,实践中也感觉到,这种响应式的,与当时的Vue/微信小程序/Flutter中思想大同小异,可能是未来的一种原生写UI的趋势。在现在的每记和脚印项目中,新实现的页面,都会优先考虑用Compose去实现。然而,Compose的一些性能优化点及注意点,也是做为开发人员需要熟悉的,今天将做一个小的总结。

一、声明式 vs 指令式编程
1、定义

无论是官网文档还是介绍Compose的优点时,都会说到Compose是声明式的。我们来回顾下,在wiki上有着如下定义:

声明式编程(英语:Declarative programming)或译为声明式编程,是对与命令式编程不同的编程范型的一种合称。它们建造计算机程序的结构和元素,表达计算的逻辑而不用描述它的控制流程。

指令式编程(英语:Imperative programming);是一种描述电脑所需作出的行为的编程范型。几乎所有电脑的硬件都是指令式工作;几乎所有电脑的硬件都是能执行机器语言,而机器代码是使用指令式的风格来写的。

通俗的来说就是:声明式编程是一种把程序写成描述结果的形式,而不是如何获得结果的形式。它主要关注结果,而不是实现细节。声明式编程的代码通常更简洁,更容易理解和维护。

命令式编程则是一种把程序写成指令的形式,告诉计算机如何实现结果。它更加关注细节,如何实现任务。命令式编程的代码通常更长,更难理解和维护。

2、个人理解

Compose其实就是UI框架,它最主要的功能就是让开发人员更加快速的实现 页面逻辑&交互效果 这是目的。

对于传统的XML来说,我们通过请求去服务器获取数据,请求成功后,我们需要findViewById找到页面元素View,再设置View的属性,更新页面展示状态。整个过程是按 http请求 -> 响应 -> 寻找对应View -> 更新对应View按部就班就地执行,这种思想就是命令式编程。

但是Compose描述为 http请求 -> 响应 -> 更新mutableData -> 引用对应数据的View自动重组,整个过程不需要我们开发去写更新UI的代码(发出命令),而是数据发生改变,UI界面自动更新,可以理解为声明式。

二、Compose优势

目前对于我的体验感受来说,Compose的优势体现在以下几个点:

  • 页面架构清晰。对比以前mvp,mvvm或结合viewbinding,少去了很多接口及编写填充数据相关的代码
  • 动画API简单好用。强大的动画支持,使得写动画非常简单。
  • 开发效率高,写UI速度快,style、shape等样式使用简单。
  • 另外、还有一些官方优势介绍
三、Compose 的重组作用域

虽然Compose 编译器在背后做了大量工作来保证 recomposition 范围尽可能小,我们还是需要对哪些情况发生了重组以及重组的范围有一定的了解 。

假设有如下代码:

@Composable
fun Foo() {
var text by remember { mutableStateOf(“”) }
Log.d(TAG, “Foo”)
Button(onClick = {
text = “$text $text”
}.also { Log.d(TAG, “Button”) }) {
Log.d(TAG, “Button content lambda”)
Text(text).also { Log.d(TAG, “Text”) }
}
}

其打印结果为:

D/Compose: Button content lambda
D/Compose: Text

按照开发经验,第一感觉会是,text变量只被Text控件用到了。

分析一下,Button控件的定义为:

参数 text 作为表达式执行的调用处是 Button 的尾lambda,而后才作为参数传入 Text()。 所以此时最小重组范围是 Button 的 尾lambda 而非 Text()

另外还有两点需要关注:

  • Compose 关心的是代码块中是否有对 state 的 read,而不是 write。
  • text 指向的 MutableState 实例是永远不会变的,变的只是内部的 value
重组中的 Inline 陷阱!

非inline函数 才有资格成为重组的最小范围,理解这点特别重要!

我们将代码稍作改动,为 Text() 包裹一个 Box{...}

@Composable
fun Foo() {

var text by remember { mutableStateOf(“”) }

Button(onClick = { text = “$text $text” }) {
Log.d(TAG, “Button content lambda”)
Box {
Log.d(TAG, “Box”)
Text(text).also { Log.d(TAG, “Text”) }
}
}
}

日志如下:

D/Compose: Button content lambda
D/Compose: Box
D/Compose: Text

要点

  • ColumnRowBox 乃至 Layout 这种容器类 Composable 都是 inline 函数,因此它们只能共享调用方的重组范围,也就是 Button 的 尾lambda

如果你希望通过缩小重组范围提高性能怎么办?

@Composable
fun Foo() {

var text by remember { mutableStateOf(“”) }

Button(onClick = { text = “$text $text” }) {
Log.d(TAG, “Button content lambda”)
Wrapper {
Text(text).also { Log.d(TAG, “Text”) }
}
}
}

@Composable
fun Wrapper(content: @Composable () -> Unit) {
Log.d(TAG, “Wrapper recomposing”)
Box {
Log.d(TAG, “Box”)
content()
}
}

  • 自定义非 inline 函数,使之满足 Compose 重组范围最小化条件。
四、Compose开发时,提高性能的关注点

当 Compose 更新重组时,它会经历三个阶段(跟传统View比较类似):

  • 组合:Compose 确定要显示的内容 - 运行可组合函数并构建界面树。
  • 布局:Compose 确定界面树中每个元素的尺寸和位置
  • 绘图:Compose 实际渲染各个界面元素。

基于这3个阶段, 尽可能从可组合函数中移除计算。每当界面发生变化时,都可能需要重新运行可组合函数;可能对于动画的每一帧,都会重新执行您在可组合函数中放置的所有代码。

1、合理使用 remember

它的作用是:

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

引用官方的栗子🍭:

@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中,再使用collectAsStateLanchEffect等方式进行观测自动重组。
2、使用LazyColumn、LazyRow等列表组件时,指定key

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

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

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

资源分享

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

网上学习 Android的资料一大堆,但如果学到的知识不成体系,遇到问题时只是浅尝辄止,不再深入研究,那么很难做到真正的技术提升。希望这份系统化的技术体系对大家有一个方向参考。

2020年虽然路途坎坷,都在说Android要没落,但是,不要慌,做自己的计划,学自己的习,竞争无处不在,每个行业都是如此。相信自己,没有做不到的,只有想不到的。祝大家2021年万事大吉。

网上学习资料一大堆,但如果学到的知识不成体系,遇到问题时只是浅尝辄止,不再深入研究,那么很难做到真正的技术提升。

需要这份系统化的资料的朋友,可以添加V获取:vip204888 (备注Android)
外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

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

,但是,不要慌,做自己的计划,学自己的习,竞争无处不在,每个行业都是如此。相信自己,没有做不到的,只有想不到的。祝大家2021年万事大吉。

网上学习资料一大堆,但如果学到的知识不成体系,遇到问题时只是浅尝辄止,不再深入研究,那么很难做到真正的技术提升。

需要这份系统化的资料的朋友,可以添加V获取:vip204888 (备注Android)
[外链图片转存中…(img-POXPGmzd-1713647758302)]

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

  • 26
    点赞
  • 13
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值