Administrator
发布于 2026-08-25 / 18 阅读
0
0

系列文章《Android 工程师的 100 篇进阶笔记》第 48 篇:Compose Multiplatform:跨平台 UI 的新可能

上一篇聊了逆向工程,这篇回到跨平台方向。之前第 30 篇聊过 KMM(Kotlin Multiplatform Mobile),那是共享业务逻辑。Compose Multiplatform 更进一步——共享 UI 代码。一套 Composable 同时跑在 Android、iOS、桌面、Web 上,这事靠谱吗?

Compose Multiplatform vs KMM

维度

KMM

Compose Multiplatform

共享内容

业务逻辑、数据层、网络层

业务逻辑 + UI

iOS UI

需要写 SwiftUI

用 Compose 写

成熟度

稳定

Android 稳定,iOS Beta

包体积

小(只共享 Kotlin 代码)

较大(含 Compose 运行时)

KMM 是"共享逻辑,各端写 UI"。Compose Multiplatform 是"逻辑和 UI 都共享"。后者野心更大,但 iOS 上的成熟度还在追赶。

支持的平台

  • Android:原生支持(Jetpack Compose 本身就是 Android 的)

  • iOS:通过 Kotlin/Native 编译,使用 Skiko(Skia for Kotlin)渲染

  • 桌面(JVM):Windows/macOS/Linux,用 JVM 运行

  • Web:通过 Kotlin/Wasm 编译为 WebAssembly

项目结构

compose-multiplatform-project/
├── build.gradle.kts
├── shared/                    # 共享模块
│   ├── build.gradle.kts
│   └── src/
│       ├── commonMain/        # 跨平台代码
│       ├── androidMain/        # Android 特定
│       └── iosMain/           # iOS 特定
├── androidApp/                # Android 入口
├── iosApp/                    # iOS 入口(Xcode 工程)
├── desktopApp/                # 桌面入口

基础用法

共享 UI 代码:

// shared/src/commonMain/kotlin/App.kt
@Composable
fun App() {
    var count by remember { mutableStateOf(0) }
    
    Column(
        modifier = Modifier.fillMaxSize(),
        horizontalAlignment = Alignment.CenterHorizontally,
        verticalArrangement = Arrangement.Center
    ) {
        Text("Count: $count")
        Button(onClick = { count++ }) {
            Text("Click me")
        }
    }
}

Android 入口:

// androidApp/src/main/java/MainActivity.kt
class MainActivity : ComponentActivity() {
    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContent {
            App()
        }
    }
}

iOS 入口:

// shared/src/iosMain/kotlin/MainViewController.kt
fun MainViewController() = ComposeUIViewController {
    App()
}
// iosApp/ContentView.swift
struct ContentView: View {
    let mainViewController = MainViewControllerKt.MainViewController()
    
    var body: some View {
        mainViewController
            .ignoresSafeArea()
    }
}

平台差异处理

UI 大部分可以共享,但有些地方必须区分平台。用 expect/actual 机制:

// commonMain
expect fun getPlatformName(): String

// androidMain
actual fun getPlatformName() = "Android"

// iosMain
actual fun getPlatformName() = "iOS"
@Composable
fun App() {
    Text("Running on ${getPlatformName()}")
}

更复杂的场景用 expect composable:

// commonMain
@Composable
expect fun PlatformCameraView(modifier: Modifier)

// androidMain
@Composable
actual fun PlatformCameraView(modifier: Modifier) {
    // Android CameraX 实现
}

// iosMain
@Composable
actual fun PlatformCameraView(modifier: Modifier) {
    // AVFoundation 实现
}

主题和资源

跨平台资源管理用版本目录或 moko-resources 库:

// 共享字符串
strings.app_name // "My App"

// 多语言
strings.hello.desc() // 根据 locale 返回

主题用 Compose 的 MaterialTheme,跨平台一致:

@Composable
fun AppTheme(content: @Composable () -> Unit) {
    MaterialTheme(
        colorScheme = if (isSystemInDarkTheme()) darkColorScheme() else lightColorScheme(),
        content = content
    )
}

iOS 上的限制和注意事项

  1. 渲染方式:iOS 上用 Skiko 渲染,不走 UIKit。这意味着 Compose 的 View 不是真正的 UIView,与原生 iOS 组件互操作有障碍。

  2. 动画性能:Skiko 渲染的性能在 iOS 上不如原生 Core Animation,复杂动画可能掉帧。

  3. 文字输入:IME 和文本选择的体验不如原生。

  4. 无障碍:iOS 的 VoiceOver 对 Compose 的支持不完善。

  5. App 体积:Skiko 运行时增加约 10-15MB。

  6. 热重载:iOS 上没有热重载,改代码要重新编译。

桌面应用

桌面端相对成熟:

// desktopApp
fun main() = application {
    Window(
        title = "My App",
        state = rememberWindowState(width = 800.dp, height = 600.dp)
    ) {
        App()
    }
}

打包用 composeDesktop.application:

// build.gradle.kts
compose.desktop.application {
    mainClass = "MainKt"
    nativeDistributions {
        targetFormats(TargetFormat.Dmg, TargetFormat.Msi, TargetFormat.Deb)
        packageName = "MyApp"
        packageVersion = "1.0.0"
    }
}

适用场景评估

适合用 Compose Multiplatform 的场景:

  1. 小团队 + 多端需求:一个人/小团队同时做 Android+iOS,共享 UI 效率高

  2. 内部工具 / B 端应用:体验要求不极端,快速出活优先

  3. 已有 KMM 项目:逻辑已共享,再加一层 UI 共享成本低

  4. 桌面 + 移动端:桌面端成熟度高,值得用

不太适合的场景:

  1. C 端高体验要求:iOS 上渲染和交互体验还达不到原生

  2. 重度依赖原生组件:地图、相机、AR 等原生组件多的话,互操作代价大

  3. iOS 团队已有 SwiftUI 代码库:迁移不划算

小结

Compose Multiplatform 是跨平台 UI 的一个有力选项,桌面端已经可以生产使用,iOS 端适合对体验要求不极致的场景。如果你已经在用 KMM 共享逻辑,加一层 Compose UI 共享是自然延伸。但如果 iOS 体验是硬指标,目前 SwiftUI 还是更好的选择。

下一篇聊聊 Lifecycle 源码:Lifecycle、LiveData 与 ViewModel 底层原理。


评论