近日 JetBrains 正式发布了 Compose Multiplatform 1.0 版,这标志其在生产环境中使用的时机已经成熟。相信有不少人对它还不太熟悉,本文通过下面 10 个热门问题带大家认识这一最新的跨平台技术。
FAQ:
- 与 Jetpack Compose 的关系?
- 是否会取代 Flutter ?
- 有何技术优势?1.0是否已稳定?
- Android Studio 还能使用吗?
- 性能怎么样?
- 生态建设如何?
- 桌面应用开发是否要引入 JVM ?
- Web 端开发是否已经成熟?
- 未来是否支持 iOS ?
- Jetpack 是否会跨平台?
正文开始前先统一一下文中的用语:
Jetpack Compose 是 Google 针对 Android 推出的新一代声明式 UI 工具包,完全基于 Kotlin 打造,天然具备了跨平台的使用基础。JetBrains 以 Jetpack Compose(后文简称 compose-android)为基础,相继发布了 compose-desktop 和 compose-web ,使 Compose 可以运行在更多不同平台。
Compose Multiplatform (后文简称 compose-jb)本质上是将 compose-desktop,compose-web 以及 compose-android 三者进行了整合,开发者可以在单个工程中使用同一套 Artifacts 开发出运行在 Android,Desktop(Windows, macOS, LInux)以及 Web 等多端的应用程序,工程中可以实现大部分代码的共享以此达到跨平台开发的目的。
所以在概念上 compose-jb 可以看做是 compose-android 的超集;在具体实现上 compose-jb 则是在 fork 了 compose-android 的源码基础上增加了对 Desktop 和 Web 侧的 API。
compose-jb 与 compose-android 同步更新,compose-jb 的 1.0 版本目前对应到 compose-android 1.1.0-beta02,因此在通用的 API 上 compose-jb 与 compose-android 时刻保持一致,不同的只是包名发生了变化,所以你可以将你的 compose-android 代码低成本地迁移到 compose-jb 工程中。
Jetpack Compose( compose-android ) | Compose Multiplatform(compose-jb) |
---|---|
androidx.compose.runtime:runtime | org.jetbrains.compose.runtime:runtime |
androidx.compose.ui:ui | org.jetbrains.compose.ui:ui |
androidx.compose.material:material | org.jetbrains.compose.material:material |
androidx.compose.fundation:fundation | org.jetbrains.compose.fundation:fundation |
compose-jb 虽由 JetBrains 发布,但是作为 Flutter 的开发者 Google 对其也是乐见其成,因为 Compose 与 Flutter 虽然都是跨平台技术,但是两者定位不同所以不存在直接竞争关系。
Flutter 的定位就是移动端跨平台解决方案,它的一切能力建设都是围绕如何更好地“一次编写、随处运行”,首要目标就是为了降低移动应用的开发成本(虽然最近也扩展到 Desktop 以及 Desktop)。
compose-jb 的首要定位是一个声明式 UI 工具包,它的目标是通过更先进的开发范式提升 UI 开发效率。由于声明式开发思想适应性广泛,所以借助 Kotlin 成为一个跨平台框架便是水到渠成的事情。 如果说是 Flutter 成就了 Dart,那么 Kotlin 则成就了 Compose ,借助 Kotlin 近年来持续高涨的的人气,Compose 的未来也充满想象空间。
compose-jb 的受众主要有两类,首先是熟悉 Kotlin 与 Compose 的 Android 开发者,他们可以把自己的产品交付至更多平台;其次是 Kotlin 经验者,他们可以使用熟悉的语言更高效地开发包含 UI 的应用程序,像 JetBrains 这样的 IDE 公司就属于后者,他们迫切希望使用 Compose 替换 Swing 和 AWT 等基于 Java 的陈旧的技术栈,这也正是 compose-desktop 诞生的初衷。
应用开发无非关注三件事:数据获取,状态管理,界面渲染。
JetBrains 推出 Kotlin Multiplatform Mobile (简称 KMM) 实现了数据获取部分的跨平台,而 compose-jb 将跨平台的范围进一步覆盖到状态管理甚至界面渲染(基于 Skia)。
在一个 compose-jb 工程中,逻辑层(状态管理)以及数据层的代码在几乎可以完全共享。在表现层,常用的组件和布局例如 Text
,Button
,Column/Row
等都可以跨越 compose-android 与 compsose-desktop 通用,此外 compose-desktop 针对桌面系统的特性还提供了专用能力,比如可以感知鼠标行为和窗口大小、创建 Scrollbars
,Tooltips
,Tray
等
fun main() {
Window {
var windowSize by remember { mutableStateOf(IntSize.Zero) }
var windowLocation by remember { mutableStateOf(IntOffset.Zero) }
AppWindowAmbient.current?.apply {
events.onResize = { windowSize = it }
events.onRelocate = { windowLocation = it }
}
Text(text = "Location: ${windowLocation}\nSize: ${windowSize}")
}
}
compose-desktop 还提供了 SwingPanel
用来嵌入使用既有的 Swing 组件。compose-desktop 在能力上完全可以替代 AWT 和 Swing 等现有 UI 框架。
compose-web 为 Web 开发者提供了专门的 DOM API,针对常用的 HTML 标签实现了对应的 Composable 组件,例如 Div
,P
,A
等等 ,同时提供了 attrs
方法以 key-value 的形式设置标签属性,一些常用属性也有专属方法;另外,基于 CSS-in-JS 技术 compose-web 允许开发者基于 DSL 定义 Style 样式。
fun main() {
renderComposable("root") {
var platform by remember { mutableStateOf("a platform") }
P {
Text("Welcome to Compose for $platform! ")
Button(attrs = { onClick { platform = "Web" } }) {
Text("...for what?")
}
}
A("https://www.jetbrains.com/lp/compose-web") {
Text("Learn more!")
}
}
}
compose-web 拥有 HTML 或 JSX 那样的结构化的变现能力,同时有具备了响应式状态管理能力,在 compose-jb 中还可以与 Desktop 和 Android 侧共享逻辑层代码。
稳定性方面,compose-jb 的大部分代码来自 Jetpack Compose,在 Android 端已经有上千款 App 接入,这足以保证其在 Android 端上的稳定性。
JetBrains 在几个月之前就将 Toolbox 应用从 C++ 和 Electron 迁移到了 compose-jb,并一直平稳运行,服务着超过 100 万的月活用户,常规的 UI 开发得到了检验。但是一些复杂功能可能还不够稳定,目前还有不少 issue 有待解决。
compose-jb 1.0 可以运行在 IntelliJ IDEA 2021.1 之后的版本中,IDEA 专门为其提供了工程向导和项目模板,指导开发者快速新建一个 compose-jb 项目。
开发者还可以通过插件市场下载 compose-desktop 侧的专用预览插件,在 Desktop 端实时预览添加 @Preview
的 Composalbe,提高开发效率。
Andorid Studio 作为 IntelliJ 平台下的 IDE ,自然也可以用于 compose-jb 项目的开发( IDEA 2021.1 对应 Android Studio Bumblebee 之后的版本)。AS 自带 Andoid 侧的预览能力,可以实时预览 UI 代码效果。此外 AS 对 Compose 的代码提示也更友好,比如非法调用 @Composable
函数时, IDE 会标红提示错误,而 IDEA 则只能在编译时发现错误。
compose-android 和 compose-desktop 都使用 Skia 这一开源图形库进行渲染。Skia 在 Chrome,Flutter 等多个项目中广泛使用,性能方面得到了验证。Skia 还能支持平台特有的硬件加速技术,例如 DirectX,Metal 和 OpenGL 等,compose-jb 为没有硬件加速的设备也提供了优化的软件渲染方案。曾经有人将 compose-desktop 与 JavaFX 进行过实际对比测试,在通常情况下两者渲染性能相当,尽在极端情况下会略逊于 JavaFX,不过已经足够优秀了。
https://dev.to/gz_k/jetpack-compose-desktop-rendering-performances-4992
Web 侧 compose-jb 会通过 Kotlin/JS 编译器将代码仍然编译成 JS 代码在浏览器运行,所以理论上与传统的 Web 开发方式在性能上没有区别,由于 CSS-in-JS 实现都是在运行时生成 CSS,仅仅在这一点上相对于直接使用 CSS 前端项目可能略有一点性能损耗。
而在逻辑代码由于使用 Kotlin 作为开发语言,代码的执行效率要明显优于基于 Node 的 Electron 等同类跨平台框架,Kotlin/JVM 也保证了在桌面侧至少有与 Java 同样的运行时性能。
compose-jb 依托 Kotin Multiplatform 的丰富类库,满足各种层面的能力开发,比如在架构、网络、数据存储等各方面都有不少优秀的的解决方案,一些代表性的项目如下:
Category | Library | Description |
---|---|---|
Architecture | Decompose | Kotlin Multiplatform lifecycle-aware business logic components (aka BLoCs) with routing functionality and pluggable UI (Jetpack Compose, SwiftUI, JS React, etc.), inspired by Badoos RIBs fork of the Uber RIBs framework |
MVIKotlin | Extendable MVI framework for Kotlin Multiplatform with powerful debugging tools (logging and time travel), inspired by Badoo MVICore library | |
redux-kotlin | Redux implementation for Kotlin (supports multiplatform JVM, native, JS, WASM) | |
Network | Ktor | Framework for quickly creating connected applications in Kotlin with minimal effort |
rsocket-kotlin | RSocket Kotlin multi-platform implementation | |
Storage | sqldelight | SQLDelight - Generates typesafe Kotlin APIs from SQL |
Kodein-DB | Multiplatform NoSQL database | |
multiplatform-settings | A Kotlin Multiplatform library for saving simple key-value data | |
Utils & Others | Reaktive | Kotlin multi-platform implementation of Reactive Extensions |
koin | A pragmatic lightweight dependency injection framework for Kotlin | |
kotlinx-datetime | KotlinX multiplatform date/time library | |
kotlin-logging | Lightweight logging framework for Kotlin. A convenient and performant logging library wrapping slf4j with Kotlin extensions |
More libraries:https://libs.kmp.icerock.dev/
compose-jb 在桌面端需要支持 Windows,macOS,Linux 等多套操作系统,基于 Kotlin/Native 的实现成本较高,因此现阶段 compose-desktop 仍然依赖 Kotlin/JVM 编译成 Java 字节码后再发布到各桌面系统。
compose-desktop 提供了专用的 Gradle 插件可以基于 jpackage 将 JVM 一同打包进各种格式的安装包,例如 Mac 的 dmg, Windows 的 msi,exe 以及 Linnux 的 deb 等,使用者无需额外安装 JDK ,可以像二进制程序一样开箱即用,此外还通过使用 jlink 技术只对 Java 模块的最小依赖进行打包以最大限度降低包体积。
$ ./gradlew package
> Task :packageDmg
WARNING: Using incubator modules: jdk.incubator.jpackage
The distribution is written to build/compose/binaries/main/dmg/DesktopApp-1.0.0.dmg
BUILD SUCCESSFUL in 11s
5 actionable tasks: 3 executed, 2 up-to-date
目前对 Kotlin/Native 编译器的适配工作也在进行中,未来 compose-desktop 有望通过切换到 Kotlin/Native 进一步提高应用的执行速度。
compose-jb 中整合 compose-web 的最主要意义是帮助 Kotlin 开发者扩大应用的发布场景。如果你已经有一个 compose-desktop 或者 compose-android 项目,那么基于 compose-web 可以把产品快速发布到 Web 端,并共享其中大部分的逻辑代码。
compose-web 提供的声明式 DOM API 相对于 HTML+JS+CSS 这一传统技术栈在开发范式上更加先进。但如果你已经有 React 等前端框架的使用经验那么此项优势就不存在了。而且 compose-web 在建设速度上要落后于 compose-desktop,部分 HTML 标签以及属性还缺少对应的 DSL 实现,所以从单纯开发一个前端应用的角度看,如果你对 Kolin 没有执念的话,更推荐使用 React 等已有的成熟框架。
即使从跨平台的角度看, desktop-web 也仍然有改善空间。desktop-web 在 API 设计上尊重原有的 HTML 开发习惯,这也导致其 DSL 上与 compose-android 和 compose-desktop 的差异较大,不利于 UI 代码的共享。据悉 JetBrains 团队已经着手开发与其他两端风格一致的 DSL ,届时可以通过 HTML5 Canvas 实现 UI 统一绘制,提高跨平台的开发体验。
compose-jb 目前没有对 iOS 端的支持,这是其成长为主流跨平台框架道路上的一个严重阻碍,因此可以大胆猜想 compose-jb 在未来一定会增加对 iOS 的支持,而且有迹象表明 JetBrains 已经偷偷开始了这方面工作。
有人就曾经在 compose-jb 工程中也发现过针对 iOS 的开发分支,而且 compose-ui 依赖的 Skiko 库也已经增加了对 iOS 的支持,理论上完全可以实现 iOS 侧渲染。由于 iOS 不使用 Kotlin/JVM ,所以在 Kotlin/Native 编译器以及 iOS 工具链等方面存在大量适配工作,毕竟 KMM 本身也还处于 alpha 阶段,iOS 端的开发体验还不理想,这可能就是 compose-ios 迟迟未发布的原因,但是未来是可以期待的。
那么在 compose-ios 还未出现的当下,如果你的应用有发布到 iOS 侧的需求,作为一个过渡方案可以先借助 KMM 推荐 D-KMP 架构实现逻辑层和数据层的代码共享,UI 侧现阶段可以使用 Swift-UI 等平台语言进行开发,等待 compose-ios 真正到来时再对 UI 代码进行迁移。
https://github.com/dbaroncelli/D-KMP-sample
很多 Android 开发者习惯于 Compose 搭配 Android 的 Jetpack 系列组件一同使用,所以当一个 compose-android 项目被迁移到 compose-jb 时,不少人希望有对应的 KMP 版本 Jetpack 库可供使用。
在前不久 Android Dev Summit 上 Andorid Jetpack 团队就这个问题进行了回答,答案是暂时没有相关计划。
https://www.youtube.com/watch?v=QreLkok3Euk&t=565s
首先 Jetpack 中一些重度依赖 Andorid 平台特性的组件不适合发布 KMP 版本,而一些平台无关的组件例如 Hilt,Room 等,虽然具备 KMP 化的基础但仍然会谨慎启动,因为当前首要任务还是保证其在 Android 端的稳定使用。虽然当前没有计划但是 Jetpack 团队也表示了未来会尝试对部分 Jetpack 库进行 KMP 改造,他们很乐于帮助开发者能更低成本地完成项目向 KMP 的迁移。
对于开发者来说,如果你的项目近期有跨平台的需求,那么在技术选型上就要避免过度依赖 Jetpack ,而要像 KMP 的 Library 倾向。比如在逻辑层优先使用 Flow 而非 LiveData 等;在数据层也可以考虑使用 SQLDelight 替代 Room 。