Featured image of post KMP 入门:把同一段逻辑跑在 Android 和 iOS 上

KMP 入门:把同一段逻辑跑在 Android 和 iOS 上

用一个阅读进度的小例子,弄明白 commonMain、平台代码和共享测试。

假设要做一个读书 App:Android 和 iOS 的界面可以各自写,但「读了 6 页,一共 10 页,进度是 60%」这条规则可以共用。Kotlin Multiplatform(KMP)让我们把这样的代码写进同一个 Kotlin 模块,再编译给两个平台使用。

代码该放在哪儿?

在一个同时面向 Android 和 iOS 的共享模块里,常见的目录是:

目录放什么
commonMain两端共用的数据模型、业务规则和计算
androidMain需要调用 Android API 的代码
iosMain多个 iOS 目标共用的平台代码
commonTest可以在各目标上运行的共享测试

可以先找一段没有系统依赖、输入和输出都很明确的逻辑。阅读进度就很合适。

动手写一个共享函数

把下面的函数放到 shared/src/commonMain/kotlin/blog/kmp/ReadingProgress.kt:

package blog.kmp

fun readingProgress(readPages: Int, totalPages: Int): Int {
    if (totalPages <= 0) return 0

    val pages = readPages.coerceIn(0, totalPages)
    return (pages.toLong() * 100 / totalPages).toInt()
}

readingProgress(6, 10) 返回 60。如果误传了负数或超过总页数,结果也会保持在 0% 到 100% 之间。这里取整数百分比,先转成 Long 可以避免乘以 100 时发生 Int 溢出。

接着在 commonTest 用 kotlin.test 写一组例子:

package blog.kmp

import kotlin.test.Test
import kotlin.test.assertEquals

class ReadingProgressTest {
    @Test
    fun handlesNormalAndOutOfRangeValues() {
        assertEquals(60, readingProgress(6, 10))
        assertEquals(0, readingProgress(-2, 10))
        assertEquals(100, readingProgress(12, 10))
        assertEquals(0, readingProgress(3, 0))
    }
}

如果模板还没配置测试依赖,可以在共享模块的 kotlin { sourceSets { ... } } 里加上 commonTest.dependencies { implementation(kotlin("test")) },然后在 IDE 里用项目已有的目标运行测试。

两端做法不同时怎么办?

比如调试页想在共享代码里显示当前平台,可以用 expect 声明需要的函数,再用 actual 为每个平台提供实现。把下面三段分别放在对应目录,文件里的包名保持一致:

// commonMain/kotlin/blog/kmp/Platform.kt
package blog.kmp

expect fun platformName(): String
// androidMain/kotlin/blog/kmp/Platform.kt
package blog.kmp

actual fun platformName(): String = "Android"
// iosMain/kotlin/blog/kmp/Platform.kt
package blog.kmp

actual fun platformName(): String = "iOS"

下一步可以给读书 App 加一条「读完才标记完成」的规则,把它写在 commonMain,再用两端相同的测试数据跑一遍。从小函数开始,比一上来搬整个应用更容易看清共享代码的边界。

延伸阅读:KMP 项目结构、共享测试依赖、expect 与 actual。

粤ICP备2026173006号