降Compose十八掌之『潜龙勿用』| Thinking in Compose

公众号「稀有猿诉」

Jetpack Compose是Android平台的现代化的声明式UI框架。它提供了一套声明式API,可以不必再机械式的操作View就能构建 出UI,从而更容易的构建出应用的UI,并且易于维护,易于扩展。今天我们重点理解一下Jetpack Compose背后的思想,学会以声明式思维来思考,进而写出更加专业的声明式UI代码。

从过程式UI到声明式UI

通过前面的学习,我们已经知道声明式UI的特点了,它是一坨一坨的函数。状态(要显示给用户的数据)作为参数,再写出使用这些状态的函数,然后Compose会运行这些函数最终生成UI。不必关心函数具体的执行,以及执行的结果,我们只需要描述好需要显示什么就可以了,简言之就是定义好如何使用数据的函数即可。这就是声明式UI的核心思想,它让开发者把精力放在你想要展示什么,而非繁杂的如何展示。

实际上这是函数式编程范式的一种,时刻记着我们都是在定义函数就可以了。

Compose的工作原理

传统的过程式UI(比如Android中的View和XML)是基于OO(面向对象)的,每个元素都是一个对象,它有一些属性,要构建出一颗View tree,然后当有事件变化或者有数据更新时,我们会具体的刷新具体的View。但像声明式UI,比如Compose,工作方式并不是这样的,开发者写出的并不是View tree,而是一系列描述着要展示数据的函数,其余的事情都是Comose负责。

Compose会执行这些函数,收集它们的结果,生成一颗虚拟的View tree,这一步要叫做Compositon;然后再把虚拟的View tree,生成真实的与平台相关的View tree,这一步叫做渲染。当有状态变化的时候,代表数据有更新,需要刷新UI,这时Compose会重新执行一遍函数,这就叫做重组Recomposition。因为重组会发生很多次,如果每次重组都直接去更新目标平台View tree开销太大了,因此就有了虚拟View tree这一层,每次重新会重新生成一颗虚拟View tree,然后比较两颗虚拟View tree,只当差异时,再用差异去更新目标平台的View tree。

有同学举手了,说这费二遍事,性能肯定会变得更差吧?这位同学先坐下。确实多费了一道工序,但随着CPU性能越来越高,以及像virtual dom diff技术的应用,Compose本身性能的差异已经追上传统方式了。但它的优势,比如开发效率以及可扩展性却无限放大,总的来说收益还是很大的。

说到底这是函数式编程方式,那么就会有函数式编程的特点,比如说这些函数的执行顺序不一定就是开发者写的那样,再比如说这些函数可能会并行的执行,甚至有些函数会被跳过。并且呢,重组可能随时发生,而且发生的很频繁。Compose这样做是为了保证底层UI能够及时得到刷新。

那么,在使用Compose时,就有一些注意事项,比如尽可能的使用Stateless的函数,尽可能的减少副作用。以及千万不要依赖函数的执行顺序。

拆分为细粒度的函数,加强复用

Compose是以函数来搭建UI,这相比于xml方式的一个最大的好处就是这非常方便的复用,因为可以像重构代码那样,把重复的代码抽成可复用函数。要以「自上而下」函数调用的方式来构建UI。

推荐的方式是,先把整个页面划分为不同的区域,每个区域是一个函数(Composable);再把每个区域细分成为更为细节的函数;这些细节函数如果是共性的就复用。这样做的方式,不但能够做到代码结构清晰,可读性很强,且易于维护,方便扩展。非常容易找到与UI的对应,而且容易复用,如果有新的页面,把不同的细节函数组合起来就可以了。

其实以前用XML也应该这样,但毕竟XML不像代码这样方便的管理和复用,拆成过多的XML文件不但难以管理,也会影响编译速度。

可以做一下Basic layouts in Compose,这个CodeLab非常好的演示了如何「自顶而下」的用Compose来构建UI。

Preview局部而非整体

Compose的一个强大功能就是即时Preview,不必非要到设备上运行就可以看到UI效果。不过呢Preview需要数据,而且要是静态的数据,也就是需要Mock。这对于复杂的数据来说是致命的缺点。比如说像字段非常之多的列表,或者有很多特殊字段的对象,Mock起来相当的麻烦。

一个可行的办法就是要尽可能的Preview局部,数据复杂,必然对应着复杂的页面,把复杂的页面拆开,变成一系列的简单的Composable的组合,这时每个Composable对应的数据都是相对简单的,只有几个参数,这时就是可Preview的了,Mock起来就容易得多了。

具体的来说,对于像集合性UI,我们只需要预览它的一个单元项就可以了,只要一个单元项没啥问题,组合起来看集合地无非就是重复很多个;再比如对于复杂的多字段对象,划分成为几个不同的子区域,每个子区域对应一个函数,这个函数经过Preview后没啥问题,那把这些组合起来成为一个整体后也不会有太大的问题。

Preview的作用是快速预览,减少编译运行的次数,毕竟编译运行一次要慢得多,所以要以简单快捷方便为主。而并不是真的当成运行结果,最终肯定要以在手机上运行结果为准,并进行最终的调试。

总结

对于习惯了View和XML方式的同学来说,开始用Compose肯定会略有不习惯,就需要理解一下它的原理,转变成声明式UI的思维,以函数为核心来思考问题,这样才能写出比较专业的Compose代码。

References

欢迎搜索并关注 公众号「稀有猿诉」 获取更多的优质文章!

原创不易,「打赏」「点赞」「在看」「收藏」「分享」 总要有一个吧!

相关推荐
水瓶丫头站住7 小时前
安卓APP如何适配不同的手机分辨率
android·智能手机
xvch8 小时前
Kotlin 2.1.0 入门教程(五)
android·kotlin
xvch12 小时前
Kotlin 2.1.0 入门教程(七)
android·kotlin
望风的懒蜗牛12 小时前
编译Android平台使用的FFmpeg库
android
浩宇软件开发13 小时前
Android开发,待办事项提醒App的设计与实现(个人中心页)
android·android studio·android开发
ac-er888813 小时前
Yii框架中的多语言支持:如何实现国际化
android·开发语言·php
苏金标14 小时前
The maximum compatible Gradle JVM version is 17.
android
zhangphil14 小时前
Android BitmapShader简洁实现马赛克,Kotlin(一)
android·kotlin
iofomo18 小时前
Android平台从上到下,无需ROOT/解锁/刷机,应用级拦截框架的最后一环,SVC系统调用拦截。
android
我叫特踏实19 小时前
SensorManager开发参考
android·sensormanager