Android开发

Jetpack Compose(一)--介绍

2023-05-04  本文已影响0人  Felix_Zhou

该章内容摘自Jetpack Compose从入门到实践一书中,个人认为该书对Jetpack Compose的介绍比较清晰、易懂。

Jetpack Compose是什么

Jetpack Compose(简称Compose)是Android新一代UI开发框架,致力于帮助开发者用更少的代码和更直观的API完成Native UI开发。相对于传统的UI开发方式,Compose具有以下几个方面的优势:

上述优势也使得Compose一经发布就广受追捧,目前已经有包括Twitter、Airbnb在内的众多应用采用了Compose开发UI, Compose的成熟度和稳定性也得到了市场的进一步验证。图1-1是一些使用了Compose的产品方对它的评价。

谷歌为什么要推出Compose

Andorid系统自诞生至今已发展了十几个年头,这期间智能手机无论是硬件规格还是软件形态都发生了巨大变化,Android应用开发技术也在不断进步:从RecyclerView、ConstraintLayout等各种UI控件的引入,到Architecture Components这样的架构工具,甚至连开发语言也从Java切换到了Kotlin。Andorid系统自诞生至今已发展了十几个年头,这期间智能手机无论是硬件规格还是软件形态都发生了巨大变化,Android应用开发技术也在不断进步:从RecyclerView、ConstraintLayout等各种UI控件的引入,到Architecture Components这样的架构工具,甚至连开发语言也从Java切换到了Kotlin。

臃肿的父类视图控件也造成了子类视图功能的不合理。以最常见的Button类为例,为了能让按钮具备显示文字的功能,Button被设计成了继承自TextView的子类。这样的设计显然是不妥当的,TextView中许多不适于按钮的功能也会被Button一并继承下来,比如用户肯定不需要一个带有粘贴板的Button,而且随着TextView自身的能力迭代,Button有可能引入更多不必要的功能。另一方面,像Button这类基础控件只能跟随系统的升级而更新,即使发现了问题也得不到及时修复,长期下来积重难返,破窗效应也越发突出。如今很多新的视图组件都以Jetpack扩展库的形式单独发布,目的也是为了不受系统版本的制约。

类似这样的问题在Android其他传统视图控件中还有很多,究其根源还是在于设计理念的落伍。构筑在基于面向对象思想的设计理念,让各个组件在定义时都偏向于封装私有状态。开发者需要花费大量的精力去确保各组件间状态的一致性,这也是造成命令式UI代码复杂度高的根本原因。因此,谷歌开始考虑寻找一套新的UI开发方式,希望从根本上替换现有的视图体系,彻底根除上述这些问题。谷歌高级工程师Jim Sproch(见图1-4)基于其在前端开发领域丰富的工作经验,开创性地提出了借助Kotlin Compiler Plugin为Android打造声明式UI框架的想法。在他的推动下,谷歌于2017年启动了Jetpack Compose项目(后文简称Compose项目),随后越来越多的工程师加入其中,Compose项目在谷歌内部越发受到重视。

前面提到的Android传统视图体系中的一些问题,也随着Compose的出现得到了有效解决。表1-1展示了Compose与Android View的比较。


示图.jpg

这诸多优点中最大的创新还是对于声明式这一全新开发方式的采用。相对于传统的命令式开发方式,声明式开发大大提高了UI界面的开发效率。前端领域的React、Vue.js等主流开发框架都属于声明式开发框架,所以其先进性早已被广泛验证。

命令式UI与声明式UI

命令式和声明式是两种截然不同的编程范式。命令式用命令的方式告诉计算机如何去做事情(how to do),计算机通过执行命令达到结果,而声明式直接告诉计算机用户想要的结果(what to do),计算机自己去想该怎么做。

Android现有的View视图体系就属于命令式的编程范式,我们使用XML定义的布局是静态的,无法根据响应状态自行更新。开发者需要通过findViewById等获取视图对象,然后通过命令式的代码调用对象方法驱动UI变更,而Compose采用声明式编程范式,开发者只需要根据状态描述UI,当状态变化时,UI会自动更新。

也许有人会说Data Binding不是可以让XML自己“动”起来吗?没有错,Data Binding其实就是Compose诞生之前的一种声明式UI方案,谷歌曾经寄希望于通过它来提升UI编码效率。可见,声明式UI本身并非新鲜概念,而且其优势也早已被官方认可。

举个例子,如下:
传统的UI布局:

<LinearLayout>
    <TextView>
       ... 
    </TextView>
    
    <Button>
       ... 
    </Button>

</LinearLayout>

声明式UI:

Column {
    Text()
    Button()
}

数据更新UI时:
命令式:

TextView textView = findViewById(xxx);
textView.setText(xxx);

声明式:

var str = xxx
Column {
    Text(text = str)
    Button()
}

Compose API设计原则

@Composable
fun CustomButton(str: String) {
    Button(onClick = {}) {
        Text(text = str)
    }
}
@Composable
fun OutlinedTextField(
    value: String,
    onValueChange: (String) -> Unit,
    ......
)

当用户输入文字后,onValueChange会接收到响应,但是文本框文字不会自动更新,仍然需要通过唯一来源value的变更来刷新UI。

@Composable
fun HelloComposable() {
    var content by rememberSaveable { mutableStateOf("") }
    HelloContent(content = content, onContentChange = {content = it})
}

@OptIn(ExperimentalMaterial3Api::class)
@Composable
fun HelloContent(content: String, onContentChange: (String) -> Unit) {
    Column {
        Text(text = "Hello $content")
        OutlinedTextField(
            value = content,
            onValueChange = onContentChange,
            label = { Text(text = content) }
        )
    }
}

在上面的代码中,OutlinedTextField响应用户输入后,通过onContentChange更新外部状态content,当content变化时会驱动HelloContent重新执行,重组中OutlinedTextField也会显示最新的content。

单一数据源决定了Composable数据流的单向流动,数据(content)总是自上而下流动,而事件(onContentChange)总是自下而上传递

Compose与View的关系

我们都知道在传统视图体系中由View与ViewGroup构成视图树,而Compose中也有同样一颗视图树,它由LayoutNode构成,由Composition负责管理,如图所示:


示图.jpg

两种树的节点类型不同,但是它们并非全没关系,依然可以共存于一棵树中。就像DOM节点与View也不同,但是可以通过WebView显示在一棵树上,Compose也可以借助这样一个连接点挂载在View树上。使用Android Studio自带的Layout Inspector可以看到这个连接点就是ComposeView,它就是连接View与Compose的桥梁,如图所示:


示图.jpg

ComposeView有一个唯一子节点AndroidComposeView,它既是一个ViewGroup,也是LayoutNode视图树的持有者,它实现了LayoutNode视图结构与View视图结构的连接。既然AndroidComposeView已经承担了两套体系的连接,那为什么还要多一层ComposeView呢?

ComposeView继承自AbstractComposeView,而后者有三个子类,分别对应着Activity窗口、Dialog窗口与PopupWindow窗口。Android平台存在所谓Window的概念,我们在很多场景下会有多窗口需求,例如在页面中弹出一个对话窗。AbstractComposeView的子类负责Android平台各类窗口的适配并生成对应的Composition, ComposeView作为其中一个子类负责Activity窗口的适配。总体来说,ComposeView负责对Android平台的Activity窗口的适配,AndroidComposeView负责连接LayoutNode视图系统与View视图系统。如此的职责划分可以实现上层视图适配与下层窗口适配逻辑的解耦

Jetpack Compose不只是UI框架

Compose并非一个简单的SDK,它是由一系列库及配套工具组成的完整的UI解决方案,如图所示:


示图.jpg

在开发阶段,Android Studio为我们提供了代码的实时静态检查,以及对Compose UI的实时预览功能,在编译阶段,Compose Compiler Plugin会对@Composable注解进行预处理,通过插入代码,提升了编码效率。在运行阶段,Compose从上到下分为四层,每一层都可以被单独使用,在不同维度提供能力支持,如图所示:


示图.jpg

可以只使用Compose的Runtime层构建任何基于数据驱动能力的系统或类库。在这样清晰的分层结构下,我们甚至可以隔离那些平台相关代码,自底向上自己来实现跨平台的UI系统。

盖章内容摘自Jetpack Compose从入门到实践一书中,个人认为该书对Jetpack Compose的介绍比较清晰、易懂。

上一篇 下一篇

猜你喜欢

热点阅读