Android KMP 笔记

Kotlin Multiplatform 允许你使用 Kotlin 编写代码,并将其在多个平台(如 Android、iOS、Web 等)上运行。它不是一个像 Flutter 那样的UI框架,而更侧重于在不同平台间共享代码逻辑,同时保留原生UI和性能的优势。我将从核心概念讲起,并带你通过一个实战项目来理解它的工作方式。

什么是 Android KMP?

简单来说,Android KMP 通常指的是使用 Kotlin Multiplatform Mobile 进行 Android 与 iOS 的跨平台开发。它的核心思想是:在共享模块中编写一次业务逻辑(如网络请求、数据存储、状态管理等),然后在 Android 和 iOS 平台上分别编写原生用户界面,调用这些共享逻辑

这种模式的优势很明显:

  • 代码复用率高:可以节省30%-50%的开发时间,尤其是在业务逻辑复杂的场景下。
  • 原生性能与体验:UI 是原生的(Jetpack Compose 和 SwiftUI),没有性能损耗,也能直接调用平台特定功能。
  • 灵活性高:你可以选择从共享一个模块开始,逐步将整个应用迁移到 KMP,无需一次性重写所有代码。
  • 逻辑一致性:共享逻辑确保了 Android 和 iOS 上的功能和行为完全一致,减少了因双端实现不同而产生的 Bug。

KMP 实战:构建一个跨平台的用户资料页面

为了让你有更直观的感受,我们通过一个具体的案例来拆解 KMP 的开发流程。这个案例将构建一个用户资料页面,包含获取数据、共享UI组件和平台特定实现。

案例概述

我们将创建一个简单的跨平台应用,它能够:

  1. 共享业务逻辑:从一个假想的 API 获取用户资料。
  2. 共享 UI 组件:使用 Compose Multiplatform 创建一个在 Android 和 iOS 上都能显示的资料卡片。
  3. 处理平台差异 :使用 expect/actual 机制,创建一个在 Android 上是 Material Design 按钮、在 iOS 上是原生 UIButton 的平台特定按钮。

1. 项目结构

一个典型的 KMP 项目主要包含三个部分:

  • shared:这是核心模块,包含了我们所有的 Kotlin 共享代码(业务逻辑、数据模型、共享UI)。
  • androidApp :Android 应用模块,它依赖 shared 模块,并使用 Jetpack Compose 构建UI。
  • iosApp :iOS 应用模块,它依赖 shared 模块(作为一个 framework),并使用 SwiftUI 构建UI。

2. 编写共享业务逻辑 (shared)

shared 模块的 commonMain 代码集中,我们使用 Ktor 进行网络请求,用 kotlinx.coroutines 处理异步。

kotlin 复制代码
// --- 文件: shared/src/commonMain/kotlin/com/example/app/DataModels.kt ---
// 共享的数据模型
data class User(
    val id: String,
    val name: String,
    val email: String,
    val profileImageUrl: String
)

// --- 文件: shared/src/commonMain/kotlin/com/example/app/Repository.kt ---
// 共享的业务逻辑层 - 仓库
import kotlinx.coroutines.flow.Flow
import kotlinx.coroutines.flow.flow

interface UserRepository {
    suspend fun getUserProfile(): Flow<User>
}

class UserRepositoryImpl(private val api: Api) : UserRepository {
    override suspend fun getUserProfile(): Flow<User> {
        return flow { emit(api.fetchUser()) }
    }
}

// --- 文件: shared/src/commonMain/kotlin/com/example/app/Api.kt ---
// 共享的网络层接口
import io.ktor.client.HttpClient
import io.ktor.client.request.get

interface Api {
    suspend fun fetchUser(): User
}

class ApiImpl(private val client: HttpClient) : Api {
    override suspend fun fetchUser(): User {
        // 这里简化了,实际会用Ktor发起请求并解析JSON
        return client.get("https://api.example.com/user") 
    }
}

这部分代码(数据类、仓库、API接口)在 Android 和 iOS 上是完全一致且共享的。

3. 处理平台差异 (expect/actual)

有时我们需要调用平台特有的API(如获取设备ID、显示原生弹窗)。KMP 提供了 expect/actual 关键字来处理这种差异。

我们在 commonMain 中声明一个 "期待" (expect) 的函数或类。

kotlin 复制代码
// 文件: shared/src/commonMain/kotlin/com/example/app/Platform.kt
expect fun getPlatformName(): String

然后,在 androidMain 中提供 "实际" (actual) 的 Android 实现。

kotlin 复制代码
// 文件: shared/src/androidMain/kotlin/com/example/app/Platform.android.kt
actual fun getPlatformName(): String {
    return "Android ${android.os.Build.VERSION.SDK_INT}"
}

iosMain 中提供 "实际" (actual) 的 iOS 实现。

kotlin 复制代码
// 文件: shared/src/iosMain/kotlin/com/example/app/Platform.ios.kt
import platform.UIKit.UIDevice

actual fun getPlatformName(): String {
    return UIDevice.currentDevice.systemName() + " " + UIDevice.currentDevice.systemVersion
}

这样,共享代码就可以调用 getPlatformName(),而实际执行的是对应平台的代码。

4. 构建共享UI (shared 中的 Compose)

使用 Compose Multiplatform,我们可以在 shared 模块中创建 UI 组件,这些组件能直接在 Android 和 iOS 上运行。

kotlin 复制代码
// 文件: shared/src/commonMain/kotlin/com/example/app/ProfileScreen.kt
import androidx.compose.foundation.layout.*
import androidx.compose.material.*
import androidx.compose.runtime.*
import androidx.compose.ui.Modifier
import androidx.compose.ui.unit.dp
import coil.compose.AsyncImage // 注意:图片加载库可能需要平台特定配置

@Composable
fun ProfileScreen(user: User) {
    Column(
        modifier = Modifier
            .fillMaxSize()
            .padding(16.dp)
    ) {
        // 头像
        AsyncImage(
            model = user.profileImageUrl,
            contentDescription = "Profile Image",
            modifier = Modifier.size(64.dp)
        )
        Spacer(modifier = Modifier.height(8.dp))
        // 用户名
        Text(text = user.name, style = MaterialTheme.typography.h5)
        // 邮箱
        Text(text = user.email, style = MaterialTheme.typography.body1)
        Spacer(modifier = Modifier.height(16.dp))
        // 平台特定按钮,将在下一步定义
        PlatformButton(label = "More Info") {
            // 处理点击
        }
    }
}

5. 在原生项目中集成

Android (androidApp) : Android 项目可以直接使用 Jetpack Compose 调用我们刚才创建的 ProfileScreen,就像调用普通的 Composable 函数一样。

kotlin 复制代码
// 文件: androidApp/src/main/java/com/example/app/MainActivity.kt
class MainActivity : ComponentActivity() {
    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContent {
            MyAppTheme {
                // 直接使用共享的 Composable
                ProfileScreen(user = sampleUser) 
            }
        }
    }
}

iOS (iosApp) : 在 iOS 中,我们需要使用 ComposeUIViewController 将 Compose 代码包装成一个 UIViewController,然后嵌入到 SwiftUI 中。

swift 复制代码
// 文件: iosApp/iosApp/ContentView.swift
import SwiftUI
import shared // 导入共享模块

struct ContentView: View {
    let user: User // 假设从共享模块获取

    var body: some View {
        VStack {
            Text("Native SwiftUI Header")
                .font(.headline)
            // 将 Compose UI 嵌入 SwiftUI
            ComposeProfileScreen(user: user)
                .frame(height: 300)
            List {
                Text("Native SwiftUI Item 1")
                Text("Native SwiftUI Item 2")
            }
        }
    }
}

// 用于包装 Compose UI 的辅助结构体
struct ComposeProfileScreen: UIViewControllerRepresentable {
    let user: User

    func makeUIViewController(context: Context) -> UIViewController {
        // 这个函数由 KMP 插件生成,用于创建包含 Compose UI 的 ViewController
        return App_ProfileScreenKt.ProfileScreenViewController(user: user)
    }

    func updateUIViewController(_ uiViewController: UIViewController, context: Context) {}
}

可以看到,KMP 允许你将共享的 Compose UI 和原生的 SwiftUI 组件混合在同一个屏幕上,这种灵活性是 Flutter 等方案难以比拟的。

知名企业的实战经验

  • Bitkey (区块链钱包):他们使用 KMP 共享了95%的代码,包括核心的加密和业务逻辑。起初双端UI分离,后来为了提升效率并保证UI一致性,全面转向 Compose Multiplatform。他们认为 KMP 让开发团队不再割裂,感觉像一个团队在维护一个项目。
  • 阿里巴巴 (1688.com) :他们在复杂的搜索筛选业务中使用 KMP。将之前 Android 和 iOS 两套独立的逻辑(筛选模型、请求策略、埋点)统一到共享模块中。结果是人力投入减少约30%,并且彻底解决了双端逻辑不一致、排查困难的问题。
  • Netguru (电商App):他们使用 KMP 构建了一个包含 AR(增强现实)功能的家具电商App。所有业务逻辑共享,而AR这类对性能要求极高的功能则通过原生实现,再由 KMP 调用,展示了其强大的原生能力扩展性。

总结与适用场景

总的来说,KMP 特别适合以下几种情况:

  • 逻辑密集型应用:如复杂的业务工具、金融应用、需要强一致性的客户端。
  • 从单平台扩展到多平台:如果你已经有了一个成熟的 Android 应用,KMP 是将业务逻辑复用到 iOS 端的理想路径。
  • 希望逐步采用跨平台方案的团队:可以先从一个小模块开始尝试,风险低,回报可见。

它不太适合对UI 动态化要求极高 的场景(如频繁更换主题的UI),或者你希望一套代码搞定所有事情(包括UI)的场景。KMP 的理念是"在共享业务逻辑的地方共享,在需要原生体验的地方保持原生"。

相关推荐
冬奇Lab2 小时前
WMS核心机制:窗口管理与层级控制深度解析
android·源码阅读
松仔log3 小时前
JetPack——Paging
android·rxjava
城东米粉儿3 小时前
Android Kotlin DSL 笔记
android
城东米粉儿3 小时前
Android Gradle 笔记
android
城东米粉儿3 小时前
Android Monkey 笔记
android
城东米粉儿4 小时前
Android 组件化 笔记
android
编程小风筝4 小时前
Android移动端如何实现多线程编程?
android
城东米粉儿5 小时前
Android 模块化 笔记
android