News

JetBrains Toolbox 案例研究:将 100 万用户迁移到 Kotlin 和 Compose Multiplatform

Read this post in other languages:

JetBrains Toolbox 团队的团队负责人 Victor Kropp 分享了在桌面上采用 Kotlin 和 Compose Multiplatform 的故事。

JetBrains Toolbox App 是使用 JetBrains IDE 进行开发的单一入口点。 它是工具和项目的控制面板,可供快速轻松地安装和更新 JetBrains IDE。 该应用程序最初于 2015 年作为编程马拉松项目推出,现在每月为 100 万活跃用户提供服务,并帮助用户在 JetBrains 产品上提高工作效率。

本文介绍了 Toolbox 团队如何将应用程序从 C++ 和 JavaScript 迁移到 100% Kotlin 和 Compose Multiplatform,使代码更易于维护和使用,并交付具有更好运行时性能的更小工件。

Victor,能介绍一下 JetBrains Toolbox 使用的架构和技术堆栈吗?

Toolbox App 是典型的客户端-服务器应用程序。 桌面应用从服务器请求可用工具列表,显示给用户,并根据需要下载 JetBrains 产品的更新。 我们从一开始就使用 Kotlin 实现了应用程序的服务器端部分。 然而,桌面应用程序是另一回事。

在 2015 年开始为 JetBrains Toolbox 构建桌面应用时,我们使用 C++ 实现其业务逻辑,并使用 Chromium Embedded Framework 以及 React 和 HTML/CSS/JS 构建用户界面。 我们在 Kotlin 1.0 尚未发布时做出了这个选择,当时 Java 9 附带的模块化 JDK 也没有发布。 我们负担不起为我们的小型帮助应用程序捆绑数百兆字节的 Java 运行时环境 (JRE),我们也不想让用户必须手动设置环境。 所以,我们选择了一种完全不同的方法。

2021 年,我们将用户界面从 React 迁移到 Compose Multiplatform,更具体地说是 Compose for Desktop,完成了使桌面应用程序 100% Kotlin 化的最后一步。

访问 Compose for Desktop 网站

如何在产品中使用 Kotlin 及其库?

完成向 Kotlin 的迁移后,它就被用于所有地方。 除了 Compose for Desktop 为用户界面提供支持之外,我们还大量使用 kotlinx.coroutines 处理所有异步作业。 Toolbox App 操作了许多 JSON 对象,所以我们会使用 kotlinx.serialization 进行(反)序列化。

服务器端尽可能简单。 事实上,它并不是通常的 HTTP 服务器。 可安装工具的所有信息和描述(在 Toolbox 中为“提要”)均为静态生成,并作为 JSON 文件从 CDN 提供。 它们不经常变动,因此我们仅在有工具的新版本被发布到 TeamCity 持续交付管道时才对其更新。 生成器是一个简单的命令行 Kotlin 程序,作为“构建配置”(TeamCity 的作业名称)调用,在每个受支持产品的每个构建上自动触发。 第二个作业会定期合并所有新生成的提要,舍弃过时的提要,并执行验证。

为什么 Toolbox 团队决定为桌面应用程序开发使用 Kotlin?

在转向 Compose for Desktop 之前,我们使用 Chromium Embedded Framework 为 Toolbox App 构建用户界面,并通过原生 C++ 的业务逻辑轻松支持所有主要桌面操作系统。 (此后发生了很多变化,我们决定在 2020 年将所有 C++ 业务逻辑迁移到在 JVM 上运行的 Kotlin。)

在 2015 年,这些都是启动项目的绝佳选择! 我们能够重用 Ring UI 中的组件,Ring UI 是一个由 JetBrains 构建的 Web UI 组件库。 我们之前在 Web 开发和使用 React 方面也有很多经验。

但是,它也有缺点:

  • Chromium Embedded Framework 以其资源消耗闻名。 即使在空闲时,JetBrains Toolbox 也会使用至少 200 MiB 的 RAM。 即使窗口不可见,我们也无法卸载整个框架,因为这会导致用户在尝试与应用交互时出现数秒的延迟。
  • 我们需要在单个桌面应用程序中建立成熟的客户端-服务器架构。 嵌入式 Web UI 和业务逻辑是由不同人员用不同语言编写的。 这使开发流程变得较为复杂,并且需要资源在应用程序内部来回发送数兆字节的 JSON,在已经存在的数据(反)序列化上耗尽 CPU 资源。

在将应用程序迁移到 100% Kotlin 之后,这种情况得到了显著改善:

  • Compose for Desktop 大幅降低了资源密集程度。 与我们的 JavaScript 实现相比,Compose 框架提供了更好的运行时性能,并且,在后台空闲运行时,我们设法大幅减少了应用使用的 RAM。
  • 使用一种语言意味着每个开发者都可以专注于开发功能,无需切换上下文。 它更快,更不易出错,并改善了开发者之间的知识分享。 整个应用程序也对内存中的数据使用相同的表示,跳过了额外(反)序列化步骤。

您能分享一下将 Kotlin 引入 Toolbox 的经历吗?

我们面临着许多挑战。 首先,我们需要将一个已有五年历史的代码库连同其所有功能和特性迁移到不同的堆栈。 为了确保应用程序的核心按预期工作,我们迁移了所有单元测试。 但是,我们的应用程序需要很多外部依赖项,这在不同的生态系统中显然也有所不同。 有些东西在以前的实现中不起作用,而仅仅是因为有新的依赖项支持,就能直接在新的实现中运作。 同时,其他被我们视为理所当然的东西则不再有效。 在某些情况下,我们直到公开发布后才知道这些差异。 这两个类别对应的是操作系统集成的不同方面,例如系统托盘(菜单栏)图标,或代理服务器和 SSL 证书。 另一方面,它还允许我们重用 JetBrains 其他团队编写的 Kotlin 代码,例如在 Toolbox Projects(项目)选项卡中重用 IntelliJ IDEA 的项目搜索的驱动代码或特定企业设置的检测代码。

我们甚至在公开宣布之前就开始使用 Compose for Desktop,因此我们通常是第一个遇到任何框架问题的人。 作为 Compose for Desktop 的先驱,我们在一开始就注意到了各种各样的问题,并将所有问题都报告给了 Compose Multiplatform 团队的同事。 同事们非常热情,反应迅速,很快修正了所有问题。 有时,我们当天就能获得带有修正的新版本,这真的相当了不起! 在我们采用 Compose 的过程中,以及在框架上出现困惑时,这些同事都提供了很大的帮助。

我们能够完全克隆之前的设计。 第一眼看下来,相较于 HTML/CSS,Compose 提供的布局原语似乎更少,但实际上简单的水平和垂直堆栈(Compose 中的行和列)已经涵盖了我们所有需求的 99%。 最开始,Compose for Desktop 仍然缺少一些部分,比如对 SVG 图形的支持,但是 Compose 团队的同事很快就为我们满足了这些需求。

起初,我们是在整个应用程序中使用 Compose 的 Material 组件。 这些组件都经过了精心设计,但专注于触摸界面。 这意味着所有元素都有大内边距(易于手指按下),没有悬停状态(触摸屏上没有这种东西),并且具有非常突出的视觉触摸反馈。 在桌面上就完全是另一回事了,因为组件在悬停时会做出反应,并且点击的视觉反馈仅影响当前元素(因为没有手指覆盖)。 因此,我们正在用我们自己更适合桌面的组件替换 Material 组件。 我们还计划在未来开源我们的组件库,敬请期待。

在选择 Compose for Desktop 之前,你们是否考虑过其他 UI 框架?

有一个备选方案是将应用程序转换为完全原生界面,但这让每个功能都需要三倍的工作量。 我们想要的是跨平台、美观且与 Kotlin 配合良好。

我们觉得 Swing 太老了,JavaFX 的使用还不够广泛。 所以我们选择了 Compose for Desktop,尽管当时它刚刚公布。 团队的直接支持和紧密的反馈循环也是一项巨大优势。

Kotlin 给产品带来的最大好处是什么?

日常工作现在简单多了。 我们在整个应用程序中使用相同的语言,这意味着团队中的开发者能够更好地共享代码和知识。 从 C++ 和 JavaScript 迁移到 Kotlin 后,编写起来也更有趣!

您对我们的读者有什么建议吗?

如果要将现有应用程序转换到新框架,那么不要低估迁移的复杂程度。 这就像是从头开始编写新的应用程序! 不仅功能需要重新实现,应用行为中的所有细微差别,无论有意无意,也都要重新实现。

我坚信 Compose for Desktop 是 2021 年创建跨平台桌面应用程序的绝佳方式。 与同类技术相比,Kotlin 提供了 JVM 上稳定可靠的生态系统,采用率远高于 Dart 和 Flutter,效率远高于带有 React/JS 的 Electron。

使用 Compose for Desktop 构建您的第一个桌面应用

Victor Kropp 是 JetBrains Toolbox App 的团队负责人。

本文英文原作者:

Sue

Sebastian Aigner

image description

Discover more