官方实战指南!Compose 项目无缝迁移 KMP

Kotlin 更新了一篇迁移指南:把一个 Jetpack Compose Android 应用迁到Kotlin Multiplatform。我们来看看是怎么回事。

先判断项目能不能迁

Android 项目不是只要用了 Kotlin 就能直接变成 KMP。

第一条判断:如果项目已经是 Kotlin + Jetpack Compose,迁移复杂度会明显降低。反过来,如果还有大量 Java、Android View、自定义 View 体系,迁移前就要先解决这些问题。

原因在 commonMain

KMP 共享代码最后要放进 commonMain,这里不能放 Java 代码,也不能直接依赖 Android Framework。你可以在 Android 里继续调用 Java,但共享层不行。

一个典型问题是这类代码:

bash 复制代码
// Android-only / JVM-only 思路
val key = Objects.hash(id, title, author)
val encoded = Uri.encode(url)
val now = java.time.Instant.now()

放在 Android 模块里没问题,搬到 commonMain 就会变成阻塞点。官方例子里,Jetcaster 就遇到了 Objects.hash()Uri.encode() 和大量 java.time 使用。

迁移前要先把这类代码分成两种:

bash 复制代码
能替换的:改成 Kotlin / Multiplatform 方案
不能替换的:隔离到 androidMain,再给 iOS / Desktop 写 actual 实现

否则,后面迁模块时会一直被编译错误打断。

依赖替换

Jetcaster 项目迁移里,大部分工作是识别 Android-only 依赖,并找到多平台替代品。

Jetcaster 里几个关键替换是:

bash 复制代码
Dagger / Hilt -> Koin 4
Coil 2 -> Coil 3
ROME -> Multiplatform RSS Parser
JUnit -> kotlin-test
java.time -> kotlin.time + kotlinx-datetime

这个顺序比直接改 Gradle 插件更稳。

比如 DI,如果项目里到处都是 Hilt 注解,先把模块改成 KMP 并不能解决问题。commonMain 不能依赖 Android 注入入口,也不能依赖 Hilt 生成代码。官方例子选择先全局迁到 Koin 4,连 Android-only 的 mobile 入口模块也一起改。

迁 Hilt 时还有一个细节:清掉 /build 目录。原因是旧的 Hilt 生成代码可能还留在构建目录里,继续影响编译。

对 Android 项目来说,可以先做一张依赖表:

bash 复制代码
库名        当前用途        是否 Android-only      KMP 替代
Hilt        DI              是                    Koin / Metro
Coil 2      图片加载         Android 侧            Coil 3
Room        数据库           可迁                  Room 2.7.0+
JUnit       单测             JVM 侧                kotlin-test
java.time   时间处理         JVM 侧                kotlinx-datetime

"先建 shared 模块"更重要。

业务模块从叶子节点开始

依赖处理完之后,才轮到模块适配。

官方示例里,Jetcaster 的简化模块关系大致是:

bash 复制代码
:mobile
:core:data
:core:data-testing
:core:domain
:core:domain-testing
:core:designsystem

迁移顺序不是从 App 壳开始,而是从依赖关系里更底层、更少被其他模块反向牵扯的模块开始。

官方给的顺序是:

bash 复制代码
:core:data
:core:data-testing
:core:domain
:core:domain-testing
:core:designsystem

这个顺序符合大多数 Android 多模块项目的真实情况。业务逻辑比 UI 更适合先共享,数据层和 domain 层比入口模块更容易切开。

一个 KMP 化后的模块通常会变成这种结构:

bash 复制代码
core/data/src/
  commonMain/
    kotlin/
  androidMain/
    kotlin/
  iosMain/
    kotlin/
  jvmMain/
    kotlin/

commonMain 放真正可共享的 Repository、数据模型、接口和业务规则。平台差异放进 androidMainiosMainjvmMain

Room 是一个很好的例子。

官方文档提到,Jetcaster 使用 Room 做数据库。Room 从 2.7.0 开始支持 Multiplatform,所以迁移不是把数据库全部重写,而是调整到 KMP 可用的写法,并用 expect/actual 处理平台相关部分。

可以用一个简化例子理解:

bash 复制代码
// commonMain
expect class DatabaseFactory {
    fun create(): AppDatabase
}

class PodcastRepository(
    private val databaseFactory: DatabaseFactory
) {
    private val database = databaseFactory.create()
}
bash 复制代码
// androidMain
actual class DatabaseFactory(
    private val context: Context
) {
    actual fun create(): AppDatabase {
        return Room.databaseBuilder(
            context,
            AppDatabase::class.java,
            "podcasts.db"
        ).build()
    }
}

这段代码说明的是边界:Repository 可以共享,数据库创建细节不能假装没有平台差异。

官方例子里还加了一个 OnlineChecker 接口,用来包住"只在 Android 检查网络连接"的事实。在 iOS 入口真正接入前,它可以先是 stub。

实战

把官方迁移过程压缩成工程动作,大概是这样:

能先替换依赖,就不要先搬模块。能先迁业务层,就不要先碰复杂 UI。能按屏幕迁,就不要把所有页面锁在一次大重构里。

KMP 迁移最怕的是把"技术方向"变成"全项目重写"。官方这个 Jetcaster 示例给出的路径更像日常工程:每一步都尽量小,每一步都保持可运行。

最后

KMP 迁移对 Android 项目来说,不仅仅是把 android {} 改成 kotlin {}

要做的是清理共享边界:依赖边界、平台边界、资源边界、UI 边界。其他的,Android、iOS、Desktop 入口只是最后接上去而已。

#Kotlin #KMP #ComposeMultiplatform #Android开发

相关推荐
tryqaaa_1 小时前
学习日志(五)【php反序列化全加例题】【pop链,字符逃逸,session,伪协议】
android·学习·php·web·pop·session
jingling5551 小时前
自建技术博客实战(三):工具专栏——地图定位、声音复刻与 rembg 抠图
android·开发语言·前端·ai·nextjs
Co_Hui1 小时前
Android:Service 启动
android
爱睡觉1112 小时前
Android 底层输入系统改造实录:把 gpio-keys "凭空捏造"成虚拟键盘
android
plainGeekDev2 小时前
XML Shape/Selector → Kotlin 动态创建
android·java·kotlin
plainGeekDev2 小时前
Java 自定义 View → Kotlin 自定义 View
android·java·kotlin
码云骑士3 小时前
Android ART运作流程
android
万能小林子3 小时前
如何将网页在线转APP?5种打包工具对比速成指南(含在线/手机/电脑方案)
android·ios·uni-app·web app·wap2app·app打包·app封装
梅塔鲁3 小时前
Kotlin成安卓开发首选
android