【Compose Multiplatform跨端声明式UI实用技术】第1课:CMP 全景解读——为什么选择声明式跨平台UI

本课是系列课程的开篇。我们将从技术本质、范式演进、平台矩阵和实战决策四个维度,建立对 CMP 的完整认知框架。本课内容基于 CMP 1.12.1。 文末附有15道配套习题与本课知识点总结。

📑 目录

  • [一、KMP 与 CMP:逻辑共享与 UI 共享的分工](#一、KMP 与 CMP:逻辑共享与 UI 共享的分工)
  • [二、声明式 UI 范式对比:React、SwiftUI、Jetpack Compose 的设计哲学](#二、声明式 UI 范式对比:React、SwiftUI、Jetpack Compose 的设计哲学)
  • [三、CMP 支持的平台矩阵](#三、CMP 支持的平台矩阵)
  • 四、关键里程碑:从实验到生产就绪
  • 五、适用场景判断
  • [六、实战案例:Respawn 与 Sony 的 CMP 实践](#六、实战案例:Respawn 与 Sony 的 CMP 实践)
  • 配套习题(15题)
  • 本课知识点总结

一、KMP 与 CMP:逻辑共享与 UI 共享的分工

理解 Compose Multiplatform 的第一步,是厘清它与 Kotlin Multiplatform 的关系。

Kotlin Multiplatform(KMP) 是 JetBrains 推出的开源技术,核心目标是让开发者用一套 Kotlin 代码库覆盖 Android、iOS、桌面、Web 和服务端。KMP 解决的是逻辑层的代码共享问题------网络请求、数据模型、业务规则、数据库访问等可以在多端复用,同时通过 Kotlin/Native 直接编译为平台原生二进制,保留原生开发的性能优势。2024年5月,Google 在 I/O 大会上宣布正式支持 KMP 用于 Android 和 iOS 之间的业务逻辑共享,KMP 已处于稳定且可用于生产环境的阶段。

Compose Multiplatform(CMP) 则是在 KMP 基础上更进一步,将 UI 层也纳入共享范围。CMP 延续了 Jetpack Compose 的声明式范式,提供了一套跨平台的 Compose Runtime、UI 组件模型和渲染能力。Google 在官方文档中也确认,借助 JetBrains 的 Compose Multiplatform,开发者可以跨平台共享界面代码。

两者的关系可以用一句话概括:KMP 提供代码共享的基础设施,CMP 在此之上实现 UI 的跨平台复用。 一个项目可以只使用 KMP,也可以使用 KMP + CMP,还可以使用 KMP + Flutter。CMP 是 KMP 的一个可选层,而非替代品。

CMP 的架构选择与 Flutter 存在根本性差异。Flutter 拥有整个渲染层,通过 Impeller 渲染引擎将渲染命令直接编译为 GPU 原语,在 iOS 上使用 Metal、Android 上使用 Vulkan,所有 widget 都通过 Dart 和 Impeller 渲染,零平台 UI 参与。而 CMP 在 Android 端直接复用 Jetpack Compose 的原生渲染管线,在 iOS、Desktop 和 Web 端则通过 Skiko(Skia for Kotlin)调用 Skia 图形库进行 Canvas 渲染。

下面是 KMP 与 CMP 的关系及渲染架构对比图:
#mermaid-svg-spmhNGXPDYk7fLoS{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;fill:#333;}@keyframes edge-animation-frame{from{stroke-dashoffset:0;}}@keyframes dash{to{stroke-dashoffset:0;}}#mermaid-svg-spmhNGXPDYk7fLoS .edge-animation-slow{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 50s linear infinite;stroke-linecap:round;}#mermaid-svg-spmhNGXPDYk7fLoS .edge-animation-fast{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 20s linear infinite;stroke-linecap:round;}#mermaid-svg-spmhNGXPDYk7fLoS .error-icon{fill:#552222;}#mermaid-svg-spmhNGXPDYk7fLoS .error-text{fill:#552222;stroke:#552222;}#mermaid-svg-spmhNGXPDYk7fLoS .edge-thickness-normal{stroke-width:1px;}#mermaid-svg-spmhNGXPDYk7fLoS .edge-thickness-thick{stroke-width:3.5px;}#mermaid-svg-spmhNGXPDYk7fLoS .edge-pattern-solid{stroke-dasharray:0;}#mermaid-svg-spmhNGXPDYk7fLoS .edge-thickness-invisible{stroke-width:0;fill:none;}#mermaid-svg-spmhNGXPDYk7fLoS .edge-pattern-dashed{stroke-dasharray:3;}#mermaid-svg-spmhNGXPDYk7fLoS .edge-pattern-dotted{stroke-dasharray:2;}#mermaid-svg-spmhNGXPDYk7fLoS .marker{fill:#333333;stroke:#333333;}#mermaid-svg-spmhNGXPDYk7fLoS .marker.cross{stroke:#333333;}#mermaid-svg-spmhNGXPDYk7fLoS svg{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;}#mermaid-svg-spmhNGXPDYk7fLoS p{margin:0;}#mermaid-svg-spmhNGXPDYk7fLoS .label{font-family:"trebuchet ms",verdana,arial,sans-serif;color:#333;}#mermaid-svg-spmhNGXPDYk7fLoS .cluster-label text{fill:#333;}#mermaid-svg-spmhNGXPDYk7fLoS .cluster-label span{color:#333;}#mermaid-svg-spmhNGXPDYk7fLoS .cluster-label span p{background-color:transparent;}#mermaid-svg-spmhNGXPDYk7fLoS .label text,#mermaid-svg-spmhNGXPDYk7fLoS span{fill:#333;color:#333;}#mermaid-svg-spmhNGXPDYk7fLoS .node rect,#mermaid-svg-spmhNGXPDYk7fLoS .node circle,#mermaid-svg-spmhNGXPDYk7fLoS .node ellipse,#mermaid-svg-spmhNGXPDYk7fLoS .node polygon,#mermaid-svg-spmhNGXPDYk7fLoS .node path{fill:#ECECFF;stroke:#9370DB;stroke-width:1px;}#mermaid-svg-spmhNGXPDYk7fLoS .rough-node .label text,#mermaid-svg-spmhNGXPDYk7fLoS .node .label text,#mermaid-svg-spmhNGXPDYk7fLoS .image-shape .label,#mermaid-svg-spmhNGXPDYk7fLoS .icon-shape .label{text-anchor:middle;}#mermaid-svg-spmhNGXPDYk7fLoS .node .katex path{fill:#000;stroke:#000;stroke-width:1px;}#mermaid-svg-spmhNGXPDYk7fLoS .rough-node .label,#mermaid-svg-spmhNGXPDYk7fLoS .node .label,#mermaid-svg-spmhNGXPDYk7fLoS .image-shape .label,#mermaid-svg-spmhNGXPDYk7fLoS .icon-shape .label{text-align:center;}#mermaid-svg-spmhNGXPDYk7fLoS .node.clickable{cursor:pointer;}#mermaid-svg-spmhNGXPDYk7fLoS .root .anchor path{fill:#333333!important;stroke-width:0;stroke:#333333;}#mermaid-svg-spmhNGXPDYk7fLoS .arrowheadPath{fill:#333333;}#mermaid-svg-spmhNGXPDYk7fLoS .edgePath .path{stroke:#333333;stroke-width:2.0px;}#mermaid-svg-spmhNGXPDYk7fLoS .flowchart-link{stroke:#333333;fill:none;}#mermaid-svg-spmhNGXPDYk7fLoS .edgeLabel{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-spmhNGXPDYk7fLoS .edgeLabel p{background-color:rgba(232,232,232, 0.8);}#mermaid-svg-spmhNGXPDYk7fLoS .edgeLabel rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-spmhNGXPDYk7fLoS .labelBkg{background-color:rgba(232, 232, 232, 0.5);}#mermaid-svg-spmhNGXPDYk7fLoS .cluster rect{fill:#ffffde;stroke:#aaaa33;stroke-width:1px;}#mermaid-svg-spmhNGXPDYk7fLoS .cluster text{fill:#333;}#mermaid-svg-spmhNGXPDYk7fLoS .cluster span{color:#333;}#mermaid-svg-spmhNGXPDYk7fLoS div.mermaidTooltip{position:absolute;text-align:center;max-width:200px;padding:2px;font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:12px;background:hsl(80, 100%, 96.2745098039%);border:1px solid #aaaa33;border-radius:2px;pointer-events:none;z-index:100;}#mermaid-svg-spmhNGXPDYk7fLoS .flowchartTitleText{text-anchor:middle;font-size:18px;fill:#333;}#mermaid-svg-spmhNGXPDYk7fLoS rect.text{fill:none;stroke-width:0;}#mermaid-svg-spmhNGXPDYk7fLoS .icon-shape,#mermaid-svg-spmhNGXPDYk7fLoS .image-shape{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-spmhNGXPDYk7fLoS .icon-shape p,#mermaid-svg-spmhNGXPDYk7fLoS .image-shape p{background-color:rgba(232,232,232, 0.8);padding:2px;}#mermaid-svg-spmhNGXPDYk7fLoS .icon-shape .label rect,#mermaid-svg-spmhNGXPDYk7fLoS .image-shape .label rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-spmhNGXPDYk7fLoS .label-icon{display:inline-block;height:1em;overflow:visible;vertical-align:-0.125em;}#mermaid-svg-spmhNGXPDYk7fLoS .node .label-icon path{fill:currentColor;stroke:revert;stroke-width:revert;}#mermaid-svg-spmhNGXPDYk7fLoS :root{--mermaid-font-family:"trebuchet ms",verdana,arial,sans-serif;} Kotlin Multiplatform (KMP)
逻辑层共享

网络/数据/业务规则
Compose Multiplatform (CMP)

UI 层共享
Android 端

复用 Jetpack Compose 原生渲染
iOS / Desktop / Web 端

Skiko 调用 Skia 渲染
可选组合
仅 KMP
KMP + CMP
KMP + Flutter

二、声明式 UI 范式对比:React、SwiftUI、Jetpack Compose 的设计哲学

CMP 的 UI 范式继承自 Jetpack Compose,而 Compose 与 React、SwiftUI、Flutter 同属声明式 UI 框架,共享相似的设计理念。但在组件定义方式上,四者存在显著差异。

Compose 的组件定义最为简洁。 以无状态组件为例,React 的函数组件需要 JSX 语法,Flutter 的 StatelessWidget 需要类继承和 build 方法,SwiftUI 的 View 结构体需要 body 属性。而 Compose 的组件定义几乎就是普通 Kotlin 函数,唯一的区别是增加 @Composable 注解:

kotlin 复制代码
@Composable
fun Greeting(name: String) {
    Text("Hello $name")
}

@Composable 注解在编译期生成辅助框架运行的代码,开发者只需编写一个没有返回值的函数即可。即使同为函数组件的 React 相比,Compose 也更胜一筹------Composable 无论定义还是使用都基于 Kotlin,使用体验更一致,而且没有返回值,连 return 都省了。

在状态管理上,Compose 的设计灵感来自 React Hooks,但实现更为统一。 React 使用 useState、useEffect 等 Hooks API,Compose 则提供 remember、mutableStateOf、LaunchedEffect 等对应物,且这些 API 完全基于 Kotlin 语言特性。

Flutter 与 CMP 的关键分歧在于渲染路径。 Flutter 使用 Dart 语言和自绘引擎 Impeller,完全绕开平台 UI 组件,在任何平台上渲染出一致的像素级效果。CMP 在 Android 上使用原生 Compose 渲染,在 iOS、桌面和 Web 上通过 Skiko 进行 Canvas 渲染。Skia 是 Chrome、Android 系统等广泛使用的通用型 2D 图形库,Skiko 是 Skia 的 Kotlin 多平台封装。

下面是声明式 UI 框架的组件定义方式对比图:
#mermaid-svg-W1HLX3ARZTU4i8W1{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;fill:#333;}@keyframes edge-animation-frame{from{stroke-dashoffset:0;}}@keyframes dash{to{stroke-dashoffset:0;}}#mermaid-svg-W1HLX3ARZTU4i8W1 .edge-animation-slow{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 50s linear infinite;stroke-linecap:round;}#mermaid-svg-W1HLX3ARZTU4i8W1 .edge-animation-fast{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 20s linear infinite;stroke-linecap:round;}#mermaid-svg-W1HLX3ARZTU4i8W1 .error-icon{fill:#552222;}#mermaid-svg-W1HLX3ARZTU4i8W1 .error-text{fill:#552222;stroke:#552222;}#mermaid-svg-W1HLX3ARZTU4i8W1 .edge-thickness-normal{stroke-width:1px;}#mermaid-svg-W1HLX3ARZTU4i8W1 .edge-thickness-thick{stroke-width:3.5px;}#mermaid-svg-W1HLX3ARZTU4i8W1 .edge-pattern-solid{stroke-dasharray:0;}#mermaid-svg-W1HLX3ARZTU4i8W1 .edge-thickness-invisible{stroke-width:0;fill:none;}#mermaid-svg-W1HLX3ARZTU4i8W1 .edge-pattern-dashed{stroke-dasharray:3;}#mermaid-svg-W1HLX3ARZTU4i8W1 .edge-pattern-dotted{stroke-dasharray:2;}#mermaid-svg-W1HLX3ARZTU4i8W1 .marker{fill:#333333;stroke:#333333;}#mermaid-svg-W1HLX3ARZTU4i8W1 .marker.cross{stroke:#333333;}#mermaid-svg-W1HLX3ARZTU4i8W1 svg{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;}#mermaid-svg-W1HLX3ARZTU4i8W1 p{margin:0;}#mermaid-svg-W1HLX3ARZTU4i8W1 .label{font-family:"trebuchet ms",verdana,arial,sans-serif;color:#333;}#mermaid-svg-W1HLX3ARZTU4i8W1 .cluster-label text{fill:#333;}#mermaid-svg-W1HLX3ARZTU4i8W1 .cluster-label span{color:#333;}#mermaid-svg-W1HLX3ARZTU4i8W1 .cluster-label span p{background-color:transparent;}#mermaid-svg-W1HLX3ARZTU4i8W1 .label text,#mermaid-svg-W1HLX3ARZTU4i8W1 span{fill:#333;color:#333;}#mermaid-svg-W1HLX3ARZTU4i8W1 .node rect,#mermaid-svg-W1HLX3ARZTU4i8W1 .node circle,#mermaid-svg-W1HLX3ARZTU4i8W1 .node ellipse,#mermaid-svg-W1HLX3ARZTU4i8W1 .node polygon,#mermaid-svg-W1HLX3ARZTU4i8W1 .node path{fill:#ECECFF;stroke:#9370DB;stroke-width:1px;}#mermaid-svg-W1HLX3ARZTU4i8W1 .rough-node .label text,#mermaid-svg-W1HLX3ARZTU4i8W1 .node .label text,#mermaid-svg-W1HLX3ARZTU4i8W1 .image-shape .label,#mermaid-svg-W1HLX3ARZTU4i8W1 .icon-shape .label{text-anchor:middle;}#mermaid-svg-W1HLX3ARZTU4i8W1 .node .katex path{fill:#000;stroke:#000;stroke-width:1px;}#mermaid-svg-W1HLX3ARZTU4i8W1 .rough-node .label,#mermaid-svg-W1HLX3ARZTU4i8W1 .node .label,#mermaid-svg-W1HLX3ARZTU4i8W1 .image-shape .label,#mermaid-svg-W1HLX3ARZTU4i8W1 .icon-shape .label{text-align:center;}#mermaid-svg-W1HLX3ARZTU4i8W1 .node.clickable{cursor:pointer;}#mermaid-svg-W1HLX3ARZTU4i8W1 .root .anchor path{fill:#333333!important;stroke-width:0;stroke:#333333;}#mermaid-svg-W1HLX3ARZTU4i8W1 .arrowheadPath{fill:#333333;}#mermaid-svg-W1HLX3ARZTU4i8W1 .edgePath .path{stroke:#333333;stroke-width:2.0px;}#mermaid-svg-W1HLX3ARZTU4i8W1 .flowchart-link{stroke:#333333;fill:none;}#mermaid-svg-W1HLX3ARZTU4i8W1 .edgeLabel{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-W1HLX3ARZTU4i8W1 .edgeLabel p{background-color:rgba(232,232,232, 0.8);}#mermaid-svg-W1HLX3ARZTU4i8W1 .edgeLabel rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-W1HLX3ARZTU4i8W1 .labelBkg{background-color:rgba(232, 232, 232, 0.5);}#mermaid-svg-W1HLX3ARZTU4i8W1 .cluster rect{fill:#ffffde;stroke:#aaaa33;stroke-width:1px;}#mermaid-svg-W1HLX3ARZTU4i8W1 .cluster text{fill:#333;}#mermaid-svg-W1HLX3ARZTU4i8W1 .cluster span{color:#333;}#mermaid-svg-W1HLX3ARZTU4i8W1 div.mermaidTooltip{position:absolute;text-align:center;max-width:200px;padding:2px;font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:12px;background:hsl(80, 100%, 96.2745098039%);border:1px solid #aaaa33;border-radius:2px;pointer-events:none;z-index:100;}#mermaid-svg-W1HLX3ARZTU4i8W1 .flowchartTitleText{text-anchor:middle;font-size:18px;fill:#333;}#mermaid-svg-W1HLX3ARZTU4i8W1 rect.text{fill:none;stroke-width:0;}#mermaid-svg-W1HLX3ARZTU4i8W1 .icon-shape,#mermaid-svg-W1HLX3ARZTU4i8W1 .image-shape{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-W1HLX3ARZTU4i8W1 .icon-shape p,#mermaid-svg-W1HLX3ARZTU4i8W1 .image-shape p{background-color:rgba(232,232,232, 0.8);padding:2px;}#mermaid-svg-W1HLX3ARZTU4i8W1 .icon-shape .label rect,#mermaid-svg-W1HLX3ARZTU4i8W1 .image-shape .label rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-W1HLX3ARZTU4i8W1 .label-icon{display:inline-block;height:1em;overflow:visible;vertical-align:-0.125em;}#mermaid-svg-W1HLX3ARZTU4i8W1 .node .label-icon path{fill:currentColor;stroke:revert;stroke-width:revert;}#mermaid-svg-W1HLX3ARZTU4i8W1 :root{--mermaid-font-family:"trebuchet ms",verdana,arial,sans-serif;} 声明式 UI 框架
React

函数组件 + JSX
Flutter

StatelessWidget + build 方法
SwiftUI

View 结构体 + body 属性
Compose

@Composable 普通 Kotlin 函数
最简洁:无需 JSX / 类继承 / 模板
状态管理:useState / useEffect
状态管理:remember / mutableStateOf / LaunchedEffect
设计灵感来源

三、CMP 支持的平台矩阵

CMP 1.12.1 支持以下平台:

平台 最低版本要求
Android Android 5.0(API level 21)
iOS iOS 14
macOS macOS 13 arm64
Windows Windows 10(x86-64, arm64)
Linux Ubuntu 20.04(x86-64, arm64)
Web 支持 WasmGC 的浏览器

所有 CMP 版本仅支持 64 位平台。

平台源代码集采用层级结构。 commonMain 承载共享 UI 和逻辑,androidMain、iosMain、desktopMain、wasmJsMain 分别处理平台入口和平台特有 API。nativeMain 和 appleMain 等中间源代码集允许在相关平台间共享特定实现。

下面是 CMP 平台矩阵与源代码集层级结构图:
#mermaid-svg-Mu7pcdaJGyGs1ry7{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;fill:#333;}@keyframes edge-animation-frame{from{stroke-dashoffset:0;}}@keyframes dash{to{stroke-dashoffset:0;}}#mermaid-svg-Mu7pcdaJGyGs1ry7 .edge-animation-slow{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 50s linear infinite;stroke-linecap:round;}#mermaid-svg-Mu7pcdaJGyGs1ry7 .edge-animation-fast{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 20s linear infinite;stroke-linecap:round;}#mermaid-svg-Mu7pcdaJGyGs1ry7 .error-icon{fill:#552222;}#mermaid-svg-Mu7pcdaJGyGs1ry7 .error-text{fill:#552222;stroke:#552222;}#mermaid-svg-Mu7pcdaJGyGs1ry7 .edge-thickness-normal{stroke-width:1px;}#mermaid-svg-Mu7pcdaJGyGs1ry7 .edge-thickness-thick{stroke-width:3.5px;}#mermaid-svg-Mu7pcdaJGyGs1ry7 .edge-pattern-solid{stroke-dasharray:0;}#mermaid-svg-Mu7pcdaJGyGs1ry7 .edge-thickness-invisible{stroke-width:0;fill:none;}#mermaid-svg-Mu7pcdaJGyGs1ry7 .edge-pattern-dashed{stroke-dasharray:3;}#mermaid-svg-Mu7pcdaJGyGs1ry7 .edge-pattern-dotted{stroke-dasharray:2;}#mermaid-svg-Mu7pcdaJGyGs1ry7 .marker{fill:#333333;stroke:#333333;}#mermaid-svg-Mu7pcdaJGyGs1ry7 .marker.cross{stroke:#333333;}#mermaid-svg-Mu7pcdaJGyGs1ry7 svg{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;}#mermaid-svg-Mu7pcdaJGyGs1ry7 p{margin:0;}#mermaid-svg-Mu7pcdaJGyGs1ry7 .label{font-family:"trebuchet ms",verdana,arial,sans-serif;color:#333;}#mermaid-svg-Mu7pcdaJGyGs1ry7 .cluster-label text{fill:#333;}#mermaid-svg-Mu7pcdaJGyGs1ry7 .cluster-label span{color:#333;}#mermaid-svg-Mu7pcdaJGyGs1ry7 .cluster-label span p{background-color:transparent;}#mermaid-svg-Mu7pcdaJGyGs1ry7 .label text,#mermaid-svg-Mu7pcdaJGyGs1ry7 span{fill:#333;color:#333;}#mermaid-svg-Mu7pcdaJGyGs1ry7 .node rect,#mermaid-svg-Mu7pcdaJGyGs1ry7 .node circle,#mermaid-svg-Mu7pcdaJGyGs1ry7 .node ellipse,#mermaid-svg-Mu7pcdaJGyGs1ry7 .node polygon,#mermaid-svg-Mu7pcdaJGyGs1ry7 .node path{fill:#ECECFF;stroke:#9370DB;stroke-width:1px;}#mermaid-svg-Mu7pcdaJGyGs1ry7 .rough-node .label text,#mermaid-svg-Mu7pcdaJGyGs1ry7 .node .label text,#mermaid-svg-Mu7pcdaJGyGs1ry7 .image-shape .label,#mermaid-svg-Mu7pcdaJGyGs1ry7 .icon-shape .label{text-anchor:middle;}#mermaid-svg-Mu7pcdaJGyGs1ry7 .node .katex path{fill:#000;stroke:#000;stroke-width:1px;}#mermaid-svg-Mu7pcdaJGyGs1ry7 .rough-node .label,#mermaid-svg-Mu7pcdaJGyGs1ry7 .node .label,#mermaid-svg-Mu7pcdaJGyGs1ry7 .image-shape .label,#mermaid-svg-Mu7pcdaJGyGs1ry7 .icon-shape .label{text-align:center;}#mermaid-svg-Mu7pcdaJGyGs1ry7 .node.clickable{cursor:pointer;}#mermaid-svg-Mu7pcdaJGyGs1ry7 .root .anchor path{fill:#333333!important;stroke-width:0;stroke:#333333;}#mermaid-svg-Mu7pcdaJGyGs1ry7 .arrowheadPath{fill:#333333;}#mermaid-svg-Mu7pcdaJGyGs1ry7 .edgePath .path{stroke:#333333;stroke-width:2.0px;}#mermaid-svg-Mu7pcdaJGyGs1ry7 .flowchart-link{stroke:#333333;fill:none;}#mermaid-svg-Mu7pcdaJGyGs1ry7 .edgeLabel{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-Mu7pcdaJGyGs1ry7 .edgeLabel p{background-color:rgba(232,232,232, 0.8);}#mermaid-svg-Mu7pcdaJGyGs1ry7 .edgeLabel rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-Mu7pcdaJGyGs1ry7 .labelBkg{background-color:rgba(232, 232, 232, 0.5);}#mermaid-svg-Mu7pcdaJGyGs1ry7 .cluster rect{fill:#ffffde;stroke:#aaaa33;stroke-width:1px;}#mermaid-svg-Mu7pcdaJGyGs1ry7 .cluster text{fill:#333;}#mermaid-svg-Mu7pcdaJGyGs1ry7 .cluster span{color:#333;}#mermaid-svg-Mu7pcdaJGyGs1ry7 div.mermaidTooltip{position:absolute;text-align:center;max-width:200px;padding:2px;font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:12px;background:hsl(80, 100%, 96.2745098039%);border:1px solid #aaaa33;border-radius:2px;pointer-events:none;z-index:100;}#mermaid-svg-Mu7pcdaJGyGs1ry7 .flowchartTitleText{text-anchor:middle;font-size:18px;fill:#333;}#mermaid-svg-Mu7pcdaJGyGs1ry7 rect.text{fill:none;stroke-width:0;}#mermaid-svg-Mu7pcdaJGyGs1ry7 .icon-shape,#mermaid-svg-Mu7pcdaJGyGs1ry7 .image-shape{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-Mu7pcdaJGyGs1ry7 .icon-shape p,#mermaid-svg-Mu7pcdaJGyGs1ry7 .image-shape p{background-color:rgba(232,232,232, 0.8);padding:2px;}#mermaid-svg-Mu7pcdaJGyGs1ry7 .icon-shape .label rect,#mermaid-svg-Mu7pcdaJGyGs1ry7 .image-shape .label rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-Mu7pcdaJGyGs1ry7 .label-icon{display:inline-block;height:1em;overflow:visible;vertical-align:-0.125em;}#mermaid-svg-Mu7pcdaJGyGs1ry7 .node .label-icon path{fill:currentColor;stroke:revert;stroke-width:revert;}#mermaid-svg-Mu7pcdaJGyGs1ry7 :root{--mermaid-font-family:"trebuchet ms",verdana,arial,sans-serif;} 源代码集层级结构
commonMain

共享 UI 与逻辑
androidMain / iosMain

平台入口与特有 API
desktopMain / wasmJsMain

平台入口与特有 API
nativeMain / appleMain

中间源集
CMP 1.12.1 平台矩阵
Android 5.0+ (API 21)
iOS 14+
macOS 13 arm64
Windows 10+
Ubuntu 20.04+
Web (WasmGC 浏览器)

四、关键里程碑:从实验到生产就绪

CMP 的成熟过程有一条清晰的时间线,理解这条线有助于你判断当前版本的技术成熟度。

2025年5月:CMP for iOS 稳定。 1.8.0 版本将 Compose for iOS 带入稳定阶段,所有主要 API 正式稳定,具有强大的兼容性保证。iOS 端的启动时间与原生应用相当,滚动性能与 SwiftUI 持平,应用体积仅增加约 9MB。CMP for iOS 的稳定标志着多年开发的成果,也解决了此前关于 UI 性能和内存管理的担忧。

2026年1月:CMP 1.10.0 发布。 这是 CMP 发展中的关键版本,带来了三个重要变化:统一的 @Preview 注解进入 commonMain;Navigation 3 在非 Android 平台获得支持;Compose Hot Reload 达到稳定状态并默认捆绑在 Gradle 插件中,要求 Kotlin 2.1.20 或更高版本。

2026年5月:CMP 1.11.0 发布。 该版本将渲染任务卸载到专用的渲染线程,无需额外配置即可获得性能收益;Web 端滚动性能显著改善,更接近原生平台的表现。

2026年8月:CMP 1.12.0 发布。 这一版本将 Skia 更新至 Milestone 150,为 iOS 惰性布局带来滚动性能改进(列表项停用在绘制阶段之外执行,使绘制阶段更快完成),为 Web 引入自动字体回退功能(未覆盖字符按需下载 Noto 字体子集),并为 Desktop 端 Compose Hot Reload 引入了实验性 MCP 服务器。MCP 服务器使 AI 编码代理能够与运行中的 Compose 应用实时交互------代理可以触发重载、截图、检查语义树、模拟点击和输入、读取应用日志,从而验证自身编辑的结果。

下面是 CMP 关键里程碑时间线图:
#mermaid-svg-SNxOHduQVhaDyrSd{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;fill:#333;}@keyframes edge-animation-frame{from{stroke-dashoffset:0;}}@keyframes dash{to{stroke-dashoffset:0;}}#mermaid-svg-SNxOHduQVhaDyrSd .edge-animation-slow{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 50s linear infinite;stroke-linecap:round;}#mermaid-svg-SNxOHduQVhaDyrSd .edge-animation-fast{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 20s linear infinite;stroke-linecap:round;}#mermaid-svg-SNxOHduQVhaDyrSd .error-icon{fill:#552222;}#mermaid-svg-SNxOHduQVhaDyrSd .error-text{fill:#552222;stroke:#552222;}#mermaid-svg-SNxOHduQVhaDyrSd .edge-thickness-normal{stroke-width:1px;}#mermaid-svg-SNxOHduQVhaDyrSd .edge-thickness-thick{stroke-width:3.5px;}#mermaid-svg-SNxOHduQVhaDyrSd .edge-pattern-solid{stroke-dasharray:0;}#mermaid-svg-SNxOHduQVhaDyrSd .edge-thickness-invisible{stroke-width:0;fill:none;}#mermaid-svg-SNxOHduQVhaDyrSd .edge-pattern-dashed{stroke-dasharray:3;}#mermaid-svg-SNxOHduQVhaDyrSd .edge-pattern-dotted{stroke-dasharray:2;}#mermaid-svg-SNxOHduQVhaDyrSd .marker{fill:#333333;stroke:#333333;}#mermaid-svg-SNxOHduQVhaDyrSd .marker.cross{stroke:#333333;}#mermaid-svg-SNxOHduQVhaDyrSd svg{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;}#mermaid-svg-SNxOHduQVhaDyrSd p{margin:0;}#mermaid-svg-SNxOHduQVhaDyrSd .label{font-family:"trebuchet ms",verdana,arial,sans-serif;color:#333;}#mermaid-svg-SNxOHduQVhaDyrSd .cluster-label text{fill:#333;}#mermaid-svg-SNxOHduQVhaDyrSd .cluster-label span{color:#333;}#mermaid-svg-SNxOHduQVhaDyrSd .cluster-label span p{background-color:transparent;}#mermaid-svg-SNxOHduQVhaDyrSd .label text,#mermaid-svg-SNxOHduQVhaDyrSd span{fill:#333;color:#333;}#mermaid-svg-SNxOHduQVhaDyrSd .node rect,#mermaid-svg-SNxOHduQVhaDyrSd .node circle,#mermaid-svg-SNxOHduQVhaDyrSd .node ellipse,#mermaid-svg-SNxOHduQVhaDyrSd .node polygon,#mermaid-svg-SNxOHduQVhaDyrSd .node path{fill:#ECECFF;stroke:#9370DB;stroke-width:1px;}#mermaid-svg-SNxOHduQVhaDyrSd .rough-node .label text,#mermaid-svg-SNxOHduQVhaDyrSd .node .label text,#mermaid-svg-SNxOHduQVhaDyrSd .image-shape .label,#mermaid-svg-SNxOHduQVhaDyrSd .icon-shape .label{text-anchor:middle;}#mermaid-svg-SNxOHduQVhaDyrSd .node .katex path{fill:#000;stroke:#000;stroke-width:1px;}#mermaid-svg-SNxOHduQVhaDyrSd .rough-node .label,#mermaid-svg-SNxOHduQVhaDyrSd .node .label,#mermaid-svg-SNxOHduQVhaDyrSd .image-shape .label,#mermaid-svg-SNxOHduQVhaDyrSd .icon-shape .label{text-align:center;}#mermaid-svg-SNxOHduQVhaDyrSd .node.clickable{cursor:pointer;}#mermaid-svg-SNxOHduQVhaDyrSd .root .anchor path{fill:#333333!important;stroke-width:0;stroke:#333333;}#mermaid-svg-SNxOHduQVhaDyrSd .arrowheadPath{fill:#333333;}#mermaid-svg-SNxOHduQVhaDyrSd .edgePath .path{stroke:#333333;stroke-width:2.0px;}#mermaid-svg-SNxOHduQVhaDyrSd .flowchart-link{stroke:#333333;fill:none;}#mermaid-svg-SNxOHduQVhaDyrSd .edgeLabel{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-SNxOHduQVhaDyrSd .edgeLabel p{background-color:rgba(232,232,232, 0.8);}#mermaid-svg-SNxOHduQVhaDyrSd .edgeLabel rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-SNxOHduQVhaDyrSd .labelBkg{background-color:rgba(232, 232, 232, 0.5);}#mermaid-svg-SNxOHduQVhaDyrSd .cluster rect{fill:#ffffde;stroke:#aaaa33;stroke-width:1px;}#mermaid-svg-SNxOHduQVhaDyrSd .cluster text{fill:#333;}#mermaid-svg-SNxOHduQVhaDyrSd .cluster span{color:#333;}#mermaid-svg-SNxOHduQVhaDyrSd div.mermaidTooltip{position:absolute;text-align:center;max-width:200px;padding:2px;font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:12px;background:hsl(80, 100%, 96.2745098039%);border:1px solid #aaaa33;border-radius:2px;pointer-events:none;z-index:100;}#mermaid-svg-SNxOHduQVhaDyrSd .flowchartTitleText{text-anchor:middle;font-size:18px;fill:#333;}#mermaid-svg-SNxOHduQVhaDyrSd rect.text{fill:none;stroke-width:0;}#mermaid-svg-SNxOHduQVhaDyrSd .icon-shape,#mermaid-svg-SNxOHduQVhaDyrSd .image-shape{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-SNxOHduQVhaDyrSd .icon-shape p,#mermaid-svg-SNxOHduQVhaDyrSd .image-shape p{background-color:rgba(232,232,232, 0.8);padding:2px;}#mermaid-svg-SNxOHduQVhaDyrSd .icon-shape .label rect,#mermaid-svg-SNxOHduQVhaDyrSd .image-shape .label rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-SNxOHduQVhaDyrSd .label-icon{display:inline-block;height:1em;overflow:visible;vertical-align:-0.125em;}#mermaid-svg-SNxOHduQVhaDyrSd .node .label-icon path{fill:currentColor;stroke:revert;stroke-width:revert;}#mermaid-svg-SNxOHduQVhaDyrSd :root{--mermaid-font-family:"trebuchet ms",verdana,arial,sans-serif;} 2025年5月

CMP 1.8.0

iOS 稳定
2026年1月

CMP 1.10.0

@Preview / Navigation 3 / Hot Reload
2026年5月

CMP 1.11.0

渲染线程卸载 / Web 滚动优化
2026年8月

CMP 1.12.0

Skia M150 / 字体回退 / MCP 服务器
生产就绪

跨平台 UI 共享

五、适用场景判断

CMP 并非万能方案,明确它的适用边界比盲目采用更重要。

适合 CMP 的场景:

工具类应用是 CMP 的理想场景。笔记、待办、Markdown 编辑器、数据看板等应用,UI 结构相对稳定,交互以表单、列表、筛选、编辑为主,对平台原生体验的依赖度较低。这类应用可以从 CMP 的高代码共享率中获得最大的开发效率收益。

中后台或桌面管理端同样适合。页面结构稳定,交互模式统一,且通常不需要深度集成平台特有的硬件能力。桌面端 CMP 支持 Swing 互操作(通过 SwingPanel 包装器),可以逐步迁移已有 Swing 组件。

品牌一致性要求高的产品能从 CMP 中受益。当产品需要在多个平台上呈现完全一致的视觉风格和交互体验时,CMP 的共享 UI 天然满足这一需求。

需要谨慎评估的场景:

重度依赖平台原生体验的应用需要仔细权衡。CMP 在 iOS 端已经做了大量适配------滚动行为匹配原生物理效果、支持原生文本选择和从右到左布局、集成系统拖放、遵循系统字体大小和对比度设置。但 CMP 在 iOS 端仍有需要注意的地方,包括自定义字体和键盘适配等。如果应用的核心价值建立在深度平台集成上,混合 UI 策略可能比全共享 UI 更合适。

对包体积极度敏感的场景需要关注。CMP iOS 应用相比纯原生 SwiftUI 应用增加约 9MB,对于大多数应用而言可以接受,但在极端受限的环境中需要评估。

从行业格局来看,KMP 在 2026 年的跨平台市场中约占 23% 的份额,Flutter 占 46%,React Native 占 35%-42%。随着 CMP for iOS 进入稳定期,它在中大型互联网项目中正成为 Flutter 的有力竞争者。

下面是 CMP 适用场景决策流程图:
#mermaid-svg-RboeloTsW9J3jRw8{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;fill:#333;}@keyframes edge-animation-frame{from{stroke-dashoffset:0;}}@keyframes dash{to{stroke-dashoffset:0;}}#mermaid-svg-RboeloTsW9J3jRw8 .edge-animation-slow{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 50s linear infinite;stroke-linecap:round;}#mermaid-svg-RboeloTsW9J3jRw8 .edge-animation-fast{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 20s linear infinite;stroke-linecap:round;}#mermaid-svg-RboeloTsW9J3jRw8 .error-icon{fill:#552222;}#mermaid-svg-RboeloTsW9J3jRw8 .error-text{fill:#552222;stroke:#552222;}#mermaid-svg-RboeloTsW9J3jRw8 .edge-thickness-normal{stroke-width:1px;}#mermaid-svg-RboeloTsW9J3jRw8 .edge-thickness-thick{stroke-width:3.5px;}#mermaid-svg-RboeloTsW9J3jRw8 .edge-pattern-solid{stroke-dasharray:0;}#mermaid-svg-RboeloTsW9J3jRw8 .edge-thickness-invisible{stroke-width:0;fill:none;}#mermaid-svg-RboeloTsW9J3jRw8 .edge-pattern-dashed{stroke-dasharray:3;}#mermaid-svg-RboeloTsW9J3jRw8 .edge-pattern-dotted{stroke-dasharray:2;}#mermaid-svg-RboeloTsW9J3jRw8 .marker{fill:#333333;stroke:#333333;}#mermaid-svg-RboeloTsW9J3jRw8 .marker.cross{stroke:#333333;}#mermaid-svg-RboeloTsW9J3jRw8 svg{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;}#mermaid-svg-RboeloTsW9J3jRw8 p{margin:0;}#mermaid-svg-RboeloTsW9J3jRw8 .label{font-family:"trebuchet ms",verdana,arial,sans-serif;color:#333;}#mermaid-svg-RboeloTsW9J3jRw8 .cluster-label text{fill:#333;}#mermaid-svg-RboeloTsW9J3jRw8 .cluster-label span{color:#333;}#mermaid-svg-RboeloTsW9J3jRw8 .cluster-label span p{background-color:transparent;}#mermaid-svg-RboeloTsW9J3jRw8 .label text,#mermaid-svg-RboeloTsW9J3jRw8 span{fill:#333;color:#333;}#mermaid-svg-RboeloTsW9J3jRw8 .node rect,#mermaid-svg-RboeloTsW9J3jRw8 .node circle,#mermaid-svg-RboeloTsW9J3jRw8 .node ellipse,#mermaid-svg-RboeloTsW9J3jRw8 .node polygon,#mermaid-svg-RboeloTsW9J3jRw8 .node path{fill:#ECECFF;stroke:#9370DB;stroke-width:1px;}#mermaid-svg-RboeloTsW9J3jRw8 .rough-node .label text,#mermaid-svg-RboeloTsW9J3jRw8 .node .label text,#mermaid-svg-RboeloTsW9J3jRw8 .image-shape .label,#mermaid-svg-RboeloTsW9J3jRw8 .icon-shape .label{text-anchor:middle;}#mermaid-svg-RboeloTsW9J3jRw8 .node .katex path{fill:#000;stroke:#000;stroke-width:1px;}#mermaid-svg-RboeloTsW9J3jRw8 .rough-node .label,#mermaid-svg-RboeloTsW9J3jRw8 .node .label,#mermaid-svg-RboeloTsW9J3jRw8 .image-shape .label,#mermaid-svg-RboeloTsW9J3jRw8 .icon-shape .label{text-align:center;}#mermaid-svg-RboeloTsW9J3jRw8 .node.clickable{cursor:pointer;}#mermaid-svg-RboeloTsW9J3jRw8 .root .anchor path{fill:#333333!important;stroke-width:0;stroke:#333333;}#mermaid-svg-RboeloTsW9J3jRw8 .arrowheadPath{fill:#333333;}#mermaid-svg-RboeloTsW9J3jRw8 .edgePath .path{stroke:#333333;stroke-width:2.0px;}#mermaid-svg-RboeloTsW9J3jRw8 .flowchart-link{stroke:#333333;fill:none;}#mermaid-svg-RboeloTsW9J3jRw8 .edgeLabel{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-RboeloTsW9J3jRw8 .edgeLabel p{background-color:rgba(232,232,232, 0.8);}#mermaid-svg-RboeloTsW9J3jRw8 .edgeLabel rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-RboeloTsW9J3jRw8 .labelBkg{background-color:rgba(232, 232, 232, 0.5);}#mermaid-svg-RboeloTsW9J3jRw8 .cluster rect{fill:#ffffde;stroke:#aaaa33;stroke-width:1px;}#mermaid-svg-RboeloTsW9J3jRw8 .cluster text{fill:#333;}#mermaid-svg-RboeloTsW9J3jRw8 .cluster span{color:#333;}#mermaid-svg-RboeloTsW9J3jRw8 div.mermaidTooltip{position:absolute;text-align:center;max-width:200px;padding:2px;font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:12px;background:hsl(80, 100%, 96.2745098039%);border:1px solid #aaaa33;border-radius:2px;pointer-events:none;z-index:100;}#mermaid-svg-RboeloTsW9J3jRw8 .flowchartTitleText{text-anchor:middle;font-size:18px;fill:#333;}#mermaid-svg-RboeloTsW9J3jRw8 rect.text{fill:none;stroke-width:0;}#mermaid-svg-RboeloTsW9J3jRw8 .icon-shape,#mermaid-svg-RboeloTsW9J3jRw8 .image-shape{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-RboeloTsW9J3jRw8 .icon-shape p,#mermaid-svg-RboeloTsW9J3jRw8 .image-shape p{background-color:rgba(232,232,232, 0.8);padding:2px;}#mermaid-svg-RboeloTsW9J3jRw8 .icon-shape .label rect,#mermaid-svg-RboeloTsW9J3jRw8 .image-shape .label rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-RboeloTsW9J3jRw8 .label-icon{display:inline-block;height:1em;overflow:visible;vertical-align:-0.125em;}#mermaid-svg-RboeloTsW9J3jRw8 .node .label-icon path{fill:currentColor;stroke:revert;stroke-width:revert;}#mermaid-svg-RboeloTsW9J3jRw8 :root{--mermaid-font-family:"trebuchet ms",verdana,arial,sans-serif;} 是
否
否
是
否
是
是否适合采用 CMP?
UI 结构是否稳定?
是否重度依赖平台原生体验?
谨慎评估

混合 UI 策略可能更合适
对包体积是否极度敏感?
适合 CMP

工具类 / 中后台 / 品牌一致性
需评估 9MB 体积增量

极端受限环境慎用

六、实战案例:Respawn 与 Sony 的 CMP 实践

Respawn:96% 代码共享的独立应用。 Respawn Pro 开发了一款习惯追踪和生产力应用,其 iOS 应用使用 Compose Multiplatform 构建,与 Android 版共享了 96% 的代码。在实际项目中,CMP 的 UI 代码共享率通常在 85%--95% 之间。对于独立开发者或小团队而言,这意味着可以用接近单平台开发的工作量覆盖双平台。

Sony Auto Play:从 4 种语言到 93.7% Kotlin。 Sony 的 Auto Play 应用在 2023 年的代码库由 53% Kotlin、17% TypeScript、15% ObjC 和 13% Swift 组成,横跨 4 种语言和 3 个 IDE。迁移到 CMP 后,Kotlin 代码占比达到 93.7%。Sony 团队总结了三条迁移经验:以"实验而非采纳"的心态进行时间盒验证,每两周展示可运行软件而非幻灯片;设计非对称赌注,确保最坏情况是行业标准方案(Jetpack Compose + SwiftUI);通过 SharedKernel 边界(仅占 0.2% 的代码)连接 CMP 与遗留的 J2ObjC 代码,无需全量重写。最终,6 名工程师支撑了整个应用的跨平台开发,该应用获得了 2026 年 iF 设计奖。

企业级采用。 Netflix、McDonald's、Cash App 和挪威邮政 Posten Bring 等大型企业已成功在生产环境中部署 KMP,验证了其对关键任务应用的适用性。Google、Duolingo、Forbes、Philips、Bolt、H&M、百度、快手和哔哩哔哩等公司也采用了 KMP。

下面是实战案例与代码共享率对比图:
#mermaid-svg-OimZ5O4zQ6JSYg3Z{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;fill:#333;}@keyframes edge-animation-frame{from{stroke-dashoffset:0;}}@keyframes dash{to{stroke-dashoffset:0;}}#mermaid-svg-OimZ5O4zQ6JSYg3Z .edge-animation-slow{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 50s linear infinite;stroke-linecap:round;}#mermaid-svg-OimZ5O4zQ6JSYg3Z .edge-animation-fast{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 20s linear infinite;stroke-linecap:round;}#mermaid-svg-OimZ5O4zQ6JSYg3Z .error-icon{fill:#552222;}#mermaid-svg-OimZ5O4zQ6JSYg3Z .error-text{fill:#552222;stroke:#552222;}#mermaid-svg-OimZ5O4zQ6JSYg3Z .edge-thickness-normal{stroke-width:1px;}#mermaid-svg-OimZ5O4zQ6JSYg3Z .edge-thickness-thick{stroke-width:3.5px;}#mermaid-svg-OimZ5O4zQ6JSYg3Z .edge-pattern-solid{stroke-dasharray:0;}#mermaid-svg-OimZ5O4zQ6JSYg3Z .edge-thickness-invisible{stroke-width:0;fill:none;}#mermaid-svg-OimZ5O4zQ6JSYg3Z .edge-pattern-dashed{stroke-dasharray:3;}#mermaid-svg-OimZ5O4zQ6JSYg3Z .edge-pattern-dotted{stroke-dasharray:2;}#mermaid-svg-OimZ5O4zQ6JSYg3Z .marker{fill:#333333;stroke:#333333;}#mermaid-svg-OimZ5O4zQ6JSYg3Z .marker.cross{stroke:#333333;}#mermaid-svg-OimZ5O4zQ6JSYg3Z svg{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;}#mermaid-svg-OimZ5O4zQ6JSYg3Z p{margin:0;}#mermaid-svg-OimZ5O4zQ6JSYg3Z .label{font-family:"trebuchet ms",verdana,arial,sans-serif;color:#333;}#mermaid-svg-OimZ5O4zQ6JSYg3Z .cluster-label text{fill:#333;}#mermaid-svg-OimZ5O4zQ6JSYg3Z .cluster-label span{color:#333;}#mermaid-svg-OimZ5O4zQ6JSYg3Z .cluster-label span p{background-color:transparent;}#mermaid-svg-OimZ5O4zQ6JSYg3Z .label text,#mermaid-svg-OimZ5O4zQ6JSYg3Z span{fill:#333;color:#333;}#mermaid-svg-OimZ5O4zQ6JSYg3Z .node rect,#mermaid-svg-OimZ5O4zQ6JSYg3Z .node circle,#mermaid-svg-OimZ5O4zQ6JSYg3Z .node ellipse,#mermaid-svg-OimZ5O4zQ6JSYg3Z .node polygon,#mermaid-svg-OimZ5O4zQ6JSYg3Z .node path{fill:#ECECFF;stroke:#9370DB;stroke-width:1px;}#mermaid-svg-OimZ5O4zQ6JSYg3Z .rough-node .label text,#mermaid-svg-OimZ5O4zQ6JSYg3Z .node .label text,#mermaid-svg-OimZ5O4zQ6JSYg3Z .image-shape .label,#mermaid-svg-OimZ5O4zQ6JSYg3Z .icon-shape .label{text-anchor:middle;}#mermaid-svg-OimZ5O4zQ6JSYg3Z .node .katex path{fill:#000;stroke:#000;stroke-width:1px;}#mermaid-svg-OimZ5O4zQ6JSYg3Z .rough-node .label,#mermaid-svg-OimZ5O4zQ6JSYg3Z .node .label,#mermaid-svg-OimZ5O4zQ6JSYg3Z .image-shape .label,#mermaid-svg-OimZ5O4zQ6JSYg3Z .icon-shape .label{text-align:center;}#mermaid-svg-OimZ5O4zQ6JSYg3Z .node.clickable{cursor:pointer;}#mermaid-svg-OimZ5O4zQ6JSYg3Z .root .anchor path{fill:#333333!important;stroke-width:0;stroke:#333333;}#mermaid-svg-OimZ5O4zQ6JSYg3Z .arrowheadPath{fill:#333333;}#mermaid-svg-OimZ5O4zQ6JSYg3Z .edgePath .path{stroke:#333333;stroke-width:2.0px;}#mermaid-svg-OimZ5O4zQ6JSYg3Z .flowchart-link{stroke:#333333;fill:none;}#mermaid-svg-OimZ5O4zQ6JSYg3Z .edgeLabel{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-OimZ5O4zQ6JSYg3Z .edgeLabel p{background-color:rgba(232,232,232, 0.8);}#mermaid-svg-OimZ5O4zQ6JSYg3Z .edgeLabel rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-OimZ5O4zQ6JSYg3Z .labelBkg{background-color:rgba(232, 232, 232, 0.5);}#mermaid-svg-OimZ5O4zQ6JSYg3Z .cluster rect{fill:#ffffde;stroke:#aaaa33;stroke-width:1px;}#mermaid-svg-OimZ5O4zQ6JSYg3Z .cluster text{fill:#333;}#mermaid-svg-OimZ5O4zQ6JSYg3Z .cluster span{color:#333;}#mermaid-svg-OimZ5O4zQ6JSYg3Z div.mermaidTooltip{position:absolute;text-align:center;max-width:200px;padding:2px;font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:12px;background:hsl(80, 100%, 96.2745098039%);border:1px solid #aaaa33;border-radius:2px;pointer-events:none;z-index:100;}#mermaid-svg-OimZ5O4zQ6JSYg3Z .flowchartTitleText{text-anchor:middle;font-size:18px;fill:#333;}#mermaid-svg-OimZ5O4zQ6JSYg3Z rect.text{fill:none;stroke-width:0;}#mermaid-svg-OimZ5O4zQ6JSYg3Z .icon-shape,#mermaid-svg-OimZ5O4zQ6JSYg3Z .image-shape{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-OimZ5O4zQ6JSYg3Z .icon-shape p,#mermaid-svg-OimZ5O4zQ6JSYg3Z .image-shape p{background-color:rgba(232,232,232, 0.8);padding:2px;}#mermaid-svg-OimZ5O4zQ6JSYg3Z .icon-shape .label rect,#mermaid-svg-OimZ5O4zQ6JSYg3Z .image-shape .label rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-OimZ5O4zQ6JSYg3Z .label-icon{display:inline-block;height:1em;overflow:visible;vertical-align:-0.125em;}#mermaid-svg-OimZ5O4zQ6JSYg3Z .node .label-icon path{fill:currentColor;stroke:revert;stroke-width:revert;}#mermaid-svg-OimZ5O4zQ6JSYg3Z :root{--mermaid-font-family:"trebuchet ms",verdana,arial,sans-serif;} 代码共享率
实战案例
企业级采用
Netflix / McDonald's

Cash App / Posten Bring
Google / Duolingo / Forbes

百度 / 快手 / 哔哩哔哩
Respawn

习惯追踪应用
Sony Auto Play

车载娱乐应用
Respawn

96% 代码共享
Sony

93.7% Kotlin 占比

配套习题(15题)

第一组:KMP 与 CMP 基础(3题)

第1题 关于 KMP 与 CMP 的关系,以下描述正确的是( )

A. KMP 是 CMP 的子集,CMP 包含了 KMP 的全部功能

B. KMP 解决逻辑层代码共享,CMP 在 KMP 基础上实现 UI 层跨平台复用

C. KMP 和 CMP 是互斥的,一个项目只能选择其中之一

D. CMP 只能用于 Android 平台,KMP 用于其他平台

答案:B

解析:KMP 解决的是逻辑层的代码共享问题,CMP 是在 KMP 基础上更进一步,将 UI 层也纳入共享范围。CMP 是 KMP 的可选层,而非替代品。一个项目可以只使用 KMP,也可以使用 KMP + CMP。

第2题 2024年5月,Google 在 I/O 大会上宣布了什么与 KMP 相关的重要消息?( )

A. Google 将收购 JetBrains 的 KMP 技术

B. Google 正式支持 KMP 用于 Android 和 iOS 之间的业务逻辑共享

C. Google 宣布 KMP 不再维护

D. Google 将 KMP 集成到 Flutter 中

答案:B

解析:2024年5月,Google 在 I/O 大会上宣布正式支持使用 Kotlin Multiplatform 跨 Android 和 iOS 共享业务逻辑。KMP 本身已处于稳定阶段且可用于生产环境。

第3题 以下关于 CMP 与 Jetpack Compose 关系的说法,正确的是( )

A. CMP 是 Jetpack Compose 的替代品,两者不兼容

B. CMP 的 UI 范式继承自 Jetpack Compose,在 Android 端复用其原生渲染管线

C. CMP 只能运行在 Android 平台上

D. Jetpack Compose 是 CMP 的一个子模块

答案:B

解析:CMP 的 UI 范式继承自 Jetpack Compose,在 Android 端 CMP 直接复用 Jetpack Compose 的原生渲染管线。在 iOS、Desktop 和 Web 端则通过 Skiko 调用 Skia 图形库进行渲染。

第二组:声明式 UI 范式(3题)

第4题 与 React 的函数组件相比,Compose 组件定义的主要优势是( )

A. Compose 使用更复杂的类继承结构

B. Compose 的组件定义几乎就是普通 Kotlin 函数,只需增加 @Composable 注解

C. Compose 不支持组件复用

D. Compose 必须使用 XML 定义布局

答案:B

解析:Compose 的组件定义几乎就是普通 Kotlin 函数,唯一的区别是增加 @Composable 注解。相比之下,React 的函数组件需要 JSX 语法,Flutter 的 StatelessWidget 需要类继承和 build 方法,SwiftUI 的 View 结构体需要 body 属性。

第5题 CMP 在 iOS 和 Desktop 平台上使用的渲染引擎是( )

A. Android 原生渲染管线

B. Skiko(基于 Skia)

C. Flutter 自绘引擎 Impeller

D. WebKit 渲染引擎

答案:B

解析:CMP 在 Android 端直接复用 Jetpack Compose 的原生渲染管线。在 iOS、Desktop 和 Web 端则通过 Skiko(Skia for Kotlin)调用 Skia 图形库进行 Canvas 渲染。Skia 同时也是 Chrome 和 Android 系统的底层图形库。

第6题 Compose 的状态管理 API 设计灵感来自 React Hooks,但实现上更统一,原因是( )

A. Compose 使用 JSX 语法,与 React 完全一致

B. Compose 的状态 API 完全基于 Kotlin 语言特性,不需要 JSX 或特殊的模板语法

C. Compose 不支持状态管理

D. Compose 的状态管理必须依赖第三方库

答案:B

解析:React 使用 useState、useEffect 等 Hooks API,Compose 则提供 remember、mutableStateOf、LaunchedEffect 等对应物,且这些 API 完全基于 Kotlin 语言特性。

第三组:平台支持(3题)

第7题 CMP 1.12.1 支持的 Android 最低版本是( )

A. API level 19

B. API level 21

C. API level 24

D. API level 26

答案:B

解析:CMP 1.12.1 支持的最低平台版本中,Android 为 API level 21(Android 5.0)。这是所有 CMP 版本共同遵循的基线要求。

第8题 关于 CMP 各平台源代码集的职责,以下描述正确的是( )

A. commonMain 中可以直接调用 Android 特有的 API

B. androidMain 承载所有平台共享的 UI 代码

C. commonMain 承载共享 UI 和逻辑,平台特有 API 放在对应源集中

D. 所有平台代码都放在 commonMain 中

答案:C

解析:CMP 的平台源代码集采用层级结构。commonMain 承载共享 UI 和逻辑,androidMain、iosMain、desktopMain、wasmJsMain 分别处理平台入口和平台特有 API。平台特有的 API 必须放在对应源集中,不能在 commonMain 中直接调用。

第9题 以下哪个平台不支持 CMP?( )

A. iOS

B. Windows

C. Web(WasmGC)

D. 32位 Android 设备

答案:D

解析:所有 CMP 版本仅支持 64 位平台。CMP 支持 Android 5.0(API 21)及以上、iOS 14、macOS 13 arm64、Windows 10、Ubuntu 20.04,以及支持 WasmGC 的浏览器。32 位 Android 设备不在支持范围内。

第四组:里程碑与版本(3题)

第10题 CMP for iOS 在哪个版本达到稳定阶段?( )

A. 1.6.0

B. 1.7.0

C. 1.8.0

D. 1.10.0

答案:C

解析:2025年5月,CMP 1.8.0 版本将 Compose for iOS 带入稳定阶段。所有主要 API 正式稳定,具有强大的兼容性保证。iOS 端的启动时间与原生应用相当,滚动性能与 SwiftUI 持平,应用体积仅增加约 9MB。这一稳定标志着多年开发的成果,也解决了此前关于 UI 性能和内存管理的担忧。

第11题 CMP 1.10.0 带来的三个重要变化不包括以下哪项?( )

A. 统一的 @Preview 注解进入 commonMain

B. Navigation 3 在非 Android 平台获得支持

C. Compose Hot Reload 达到稳定状态并默认捆绑

D. Skia 更新至 Milestone 150

答案:D

解析:CMP 1.10.0 的三个重要变化是:统一的 @Preview 注解进入 commonMain;Navigation 3 在非 Android 平台获得支持;Compose Hot Reload 达到稳定状态并默认捆绑在 Gradle 插件中,要求 Kotlin 2.1.20 或更高版本。Skia 更新至 Milestone 150 是 CMP 1.12.0 的变化。

第12题 CMP 1.12.0 为 Desktop 端引入的实验性 MCP 服务器,其作用是( )

A. 替代 Gradle 构建系统

B. 让 AI 编码代理能够与运行中的 Compose 应用实时交互

C. 管理多平台资源文件

D. 自动生成 iOS 原生代码

答案:B

解析:CMP 1.12.0 为 Desktop 端 Compose Hot Reload 引入了实验性 MCP(Model Context Protocol)服务器,使 AI 编码代理能够与运行中的 Compose 应用实时交互。代理可以触发重载、截图、检查语义树、模拟点击和输入、读取应用日志,从而验证自身编辑的结果。

第五组:适用场景与案例(3题)

第13题 以下哪类应用最适合使用 CMP?( )

A. 重度依赖 AR 体验的应用

B. 笔记、待办、Markdown 编辑器等工具类应用

C. 需要深度集成平台级系统功能的应用

D. 对包体积有极端限制的嵌入式应用

答案:B

解析:工具类应用是 CMP 的理想场景。这类应用 UI 结构相对稳定,交互以表单、列表、筛选、编辑为主,对平台原生体验的依赖度较低,可以从 CMP 的高代码共享率中获得最大的开发效率收益。

第14题 Respawn iOS 应用使用 CMP 构建后,与 Android 版共享的代码比例是( )

A. 50%

B. 75%

C. 96%

D. 100%

答案:C

解析:Respawn Pro 开发了一款习惯追踪和生产力应用,其 iOS 应用使用 Compose Multiplatform 构建,与 Android 版共享了 96% 的代码。在实际项目中,CMP 的 UI 代码共享率通常在 85%--95% 之间。

第15题 Sony Auto Play 应用迁移到 CMP 后,Kotlin 代码占比达到( )

A. 53%

B. 75%

C. 93.7%

D. 100%

答案:C

解析:Sony 的 Auto Play 应用在 2023 年的代码库由 53% Kotlin、17% TypeScript、15% ObjC 和 13% Swift 组成,横跨 4 种语言和 3 个 IDE。迁移到 CMP 后,Kotlin 代码占比达到 93.7%。最终,6 名工程师支撑了整个应用的跨平台开发。

本课知识点总结

1. KMP 与 CMP 的分工

KMP 提供代码共享的基础设施,解决逻辑层的跨平台复用;CMP 在 KMP 基础上将 UI 层也纳入共享范围。CMP 是 KMP 的一个可选层。Google 于 2024 年 5 月正式宣布支持 KMP 用于 Android 和 iOS 之间的业务逻辑共享。CMP 与 Flutter 的根本差异在于渲染架构:Flutter 拥有整个渲染层(Impeller),CMP 在 Android 端复用原生渲染,在 iOS/Desktop/Web 端通过 Skiko 调用 Skia 进行 Canvas 渲染。

2. 声明式 UI 范式对比

Compose 与 React、SwiftUI、Flutter 同属声明式 UI 框架。Compose 的组件定义几乎就是普通 Kotlin 函数,只需增加 @Composable 注解,API 设计最为简洁。状态管理灵感来自 React Hooks,但完全基于 Kotlin 语言特性。

3. 平台矩阵与源集结构

CMP 1.12.1 支持 Android 5.0(API 21+)、iOS 14、macOS 13 arm64、Windows 10、Ubuntu 20.04,以及支持 WasmGC 的浏览器。所有版本仅支持 64 位平台。平台源代码集采用层级结构:commonMain 承载共享 UI 和逻辑,平台特有代码放在对应源集中。

4. 关键里程碑

CMP 的成熟时间线:2025年5月 1.8.0 版本 iOS 稳定;2026年1月 1.10.0 版本统一 @Preview、Navigation 3 跨平台支持、Hot Reload 稳定并默认捆绑;2026年5月 1.11.0 版本渲染线程卸载、Web 滚动性能改善;2026年8月 1.12.0 版本 Skia Milestone 150、iOS 惰性布局性能改进、Web 自动字体回退、Desktop 端 MCP 服务器。

5. 适用场景与实战数据

适合 CMP :工具类应用、中后台或桌面管理端、品牌一致性要求高的产品。需谨慎评估 :重度依赖平台原生体验的应用、对包体积极端敏感的场景。实战数据:Respawn iOS 应用与 Android 版共享 96% 代码;Sony Auto Play 迁移后 Kotlin 代码占比达 93.7%,仅用 6 名工程师支撑跨平台开发。Netflix、McDonald's、Cash App 等大型企业已成功在生产环境中部署 KMP。KMP 在 2026 年跨平台市场中约占 23% 的份额。


下一课预告:第2课将带你从零搭建 CMP 开发环境,使用 Kotlin Multiplatform Wizard 创建首个四端项目,并详解 AGP 9.0 时代的项目结构与 Gradle 配置要点。

相关推荐
FungLeo19 小时前
成为全栈·Flutter App 篇·分类、标签与搜索:移动端内容发现
flutter·移动开发·标签·搜索·分类管理·成为全栈
FungLeo1 天前
成为全栈·Flutter App 篇·列表分页与下拉刷新:去重、失败保留和返回位置
flutter·移动开发·下拉刷新·分页加载·列表优化·成为全栈
FungLeo4 天前
成为全栈·Flutter App 篇·go_router 与四 Tab App Shell:保留导航状态、深链和登录回跳
flutter·移动开发·成为全栈·go_router·路由设计·深链接
传奇开心果编程5 天前
【React Native娓娓道来】第7课 表单与用户输入:TextInput、表单校验与键盘处理
移动开发·表单·编程语言理论·开发范式·编程框架·声明式ui·用户输入
传奇开心果编程6 天前
【React Native娓娓道来】第9课 动画与手势:Animated、Reanimated 与 Pressable 进阶
移动开发·动画·手势·编程语言理论·开发范式·编程框架·声明式ui
传奇开心果编程6 天前
【声明式UI开发实用技术学与练】第2课 状态建模:把界面拆成可预测的数据
移动开发·编程语言理论·开发范式·编程框架·声明式ui
传奇开心果编程6 天前
【React Native娓娓道来】第3课 组件组合与列表渲染:把界面拆成可复用的函数
移动开发·列表渲染·编程语言理论·开发范式·编程框架·声明式ui·组件组合
传奇开心果编程6 天前
【声明式UI开发实用技术学与练】第6课 副作用管理:Effect 的正确用法
移动开发·编程语言理论·开发范式·编程框架·声明式ui
传奇开心果编程6 天前
【React Native娓娓道来】第4课 副作用与数据获取:useEffect、网络请求与加载状态
移动开发·数据获取·编程语言理论·开发范式·编程框架·副作用·声明式ui