MVI架构
在Jetpack Compose官方介绍视频里,设计团队提到Compose是基于MVI架构设计的,基础架构演变从MVC、MVP、MVVM都是围绕着View和Model关系来展开的,MVI也不例外,如何“高内聚,低耦合”地进行程序设计是程序员永恒的命题。
我理解的MVI可以看做是“函数式编程思想”的一种表达形式,这和RX有点类似,整个View和Model的关系是以一种流式或者说响应式的方式进行组织的(也有点像大学里学习的《信号与系统》课程中对于信号的处理过程:input -> filter -> output),用户输入或者其他数据源的变更产生了一个Intent意图(这是概念上的意图,和Android的Intent类没什么关系),Model根据意图输出State状态,View根据State进行渲染。与传统每个系统维护各自的状态不同的是,每一刻View的呈现都是根据Model的状态来驱动的,即每次发生状态的变化,都擦掉整个画布,重新根据Model产生的状态进行渲染,这样就去掉了View内部的状态,保证View的渲染肯定对Model最准确的表达,Model到View的过程是函数式的,即无状态的,View可以看做是一个filter,固定的input就有固定的output。ReactiveX其实最初的设计理念应该就是想将对事件的处理变成这样一种无状态的流式过程吧,虽然实际项目中更多的是将RX当做一种异步调度框架进行使用。
这种每一次都擦除整个画布的方式又和游戏开发中渲染过程很像,整个世界是一个mainLoop,所有的变化都是通过每一次的update来完成的,当我们决定怎么渲染这个世界后,渲染引擎就开始以流水线的方式运行,渲染管线Pipline经历Vertex,Mesh, Rasterization等等阶段,将逻辑数据经过点、线、面、纹理、光照、裁剪、栅格化等等流水线上每一个环节进行的处理最终形成了一张二维的画面,给到屏幕去显示,每一帧都在重新画一幅画。
这种方式当也能更加直观地以Declarative声明式的方式进行编码,但每一次都重新绘制整个画布的方式,在高效的GPU渲染管线上还好说(虽然也会用很多骚操作去优化处理流程),放到CPU的逻辑计算阶段代价就太大了吧,我只是想更新一个文案,难道要整个布局重新绘制一次?所以一般采用这种方式进行设计的系统都需要在重绘的范围上进行把控,Jetpack Compose所谓的Recomposition重组的概念大概就是这么个事情吧,如何准确的识别状态的变化,将重绘控制在最小的范围内,就是整个Compose引擎的目的所在。
Composable函数
@Composable
fun Greeting(name: String) {
Text(text = "Hello $name!")
}
Jetpack Compose的Hello World是以这样一个Greeting开始的,关于Composable函数的几点值得注意:
- @Composable注解和suspend关键字一样是对函数类型的一个声明。
- Composable函数必须运行在Compose环境中。
- Composable函数是函数式的,即不产生返回值,没有Side Effect副作用。
- Composable函数通过调用其他Composable函数以组合的方式进行工作,如上面这个函数中的Text也是一个Composable函数,会在页面中渲染一个文本框。
引用官方在Compose编程思想中所提到的对Composable函数的描述:
- 可组合函数可以按任何顺序执行。
- 可组合函数可以并行执行。
- 重组会跳过尽可能多的可组合函数和 lambda。
- 重组是乐观的操作,可能会被取消。
- 可组合函数可能会像动画的每一帧一样非常频繁地运行。
总结来讲,Composable函数尽量去按照函数式编程的规范不产生side effect地去编写,实际使用经验还得通过项目实践去感受呀,期待Compose能早日实装~
(以上是对Jetpack Compose不断学习探索过程中的所想,只是我个人的一个笔记,持续学习记录中,欢迎指正~)