KMP 全栈开发:从 Android 到 AI Agent 的技术文章大纲
1. 引言:为什么 KMP 能走向全栈
介绍 Kotlin Multiplatform 从移动端共享逻辑走向全栈开发的背景,说明 KMP 在 Android、iOS、服务端以及 AI Agent 场景中的定位与价值。
2. KMP 基础与工程架构
梳理 KMP 的核心概念、共享模块划分、依赖注入与分层架构,为后续全栈扩展打下基础。
下图展示了 KMP 全栈工程的整体架构:共享模块 commonMain 承载业务逻辑与数据模型,Android 与 iOS 平台层分别通过 expect/actual 机制提供平台实现,服务端模块则复用共享代码对外提供接口,各模块之间通过依赖注入保持清晰边界。
flowchart TD
A[commonMain 共享模块] --> B[androidMain Android 平台层]
A --> C[iosMain iOS 平台层]
A --> D[服务端模块]
B --> E[Android App]
C --> F[iOS App]
D --> G[后端服务]
E --> D
F --> D
3. 从 Android 到多平台:共享业务逻辑
讲解如何将 Android 项目中的网络层、数据层、领域层迁移到 KMP 共享模块,并处理平台差异。下面以网络层为例,展示如何使用 Ktor 客户端在共享模块中定义接口,并在 Android 和 iOS 中调用。
// 共享模块 commonMain 中定义网络接口
import io.ktor.client.*
import io.ktor.client.call.*
import io.ktor.client.request.*
import io.ktor.client.plugins.contentnegotiation.*
import io.ktor.serialization.kotlinx.json.*
import kotlinx.serialization.Serializable
import kotlinx.serialization.json.Json
@Serializable
data class User(val id: Int, val name: String)
// 定义统一的网络服务接口
interface UserApi {
suspend fun getUser(id: Int): User
}
// 使用 Ktor 实现该接口,平台差异通过 expect/actual 处理
class UserApiImpl(private val client: HttpClient) : UserApi {
override suspend fun getUser(id: Int): User =
client.get("https://api.example.com/users/$id").body()
}
// 创建 HttpClient 的公共工厂,引擎由各平台提供
expect fun createHttpClient(): HttpClient
// 各平台通过 actual 提供对应的引擎与配置
// Android 端(androidMain):
// actual fun createHttpClient(): HttpClient = HttpClient(Android) {
// install(ContentNegotiation) { json(Json { ignoreUnknownKeys = true }) }
// // Android 平台可在此配置网络缓存、超时等参数
// }
//
// iOS 端(iosMain):
// actual fun createHttpClient(): HttpClient = HttpClient(Darwin) {
// install(ContentNegotiation) { json(Json { ignoreUnknownKeys = true }) }
// // iOS 平台可在此配置 TLS 证书校验、代理等参数
// }
上述示例中,expect 与 actual 机制用于处理平台差异:共享模块只声明 createHttpClient() 的签名,而 Android 与 iOS 各自提供基于 Android 或 Darwin 引擎的实现,并可在其中配置平台特有的网络参数,从而在共享业务逻辑的同时保留平台灵活性。
为了更直观地对比三种方案在实现网络层、数据层和领域层时的差异,下表从代码量、维护成本和平台差异处理方式三个维度进行梳理:
| 维度 | Android 原生 | iOS 原生 | KMP 共享代码 |
|---|---|---|---|
| 网络层代码量 | 使用 Retrofit/OkHttp 编写,需为每个接口单独定义 Service 与数据模型,代码量较大。 | 使用 URLSession/Alamofire 编写,需为每个接口单独定义请求与模型,代码量较大。 | 在 commonMain 中用 Ktor 统一编写一次接口与模型,Android 与 iOS 复用,代码量显著减少。 |
| 数据层代码量 | Room 数据库、Repository 与 DAO 均需在 Android 侧单独实现。 | CoreData/SwiftData 与 Repository 均需在 iOS 侧单独实现。 | 数据模型与 Repository 逻辑在 commonMain 共享,仅数据库驱动通过 expect/actual 按平台接入。 |
| 领域层代码量 | 用例(UseCase)与业务规则在 Android 侧编写,无法复用。 | 用例与业务规则在 iOS 侧重新编写,逻辑重复。 | 领域层全部沉淀在 commonMain,业务规则只写一次,两端行为天然一致。 |
| 维护成本 | 业务逻辑变更需同步修改 Android 代码,并单独维护测试。 | 同一业务逻辑需在 iOS 侧重复实现与测试,双倍维护成本。 | 核心逻辑集中维护,改动一次即可同步两端,测试也只需在共享模块中编写一次。 |
| 平台差异处理 | 平台差异天然隔离,但无法复用,需各自处理网络、存储与线程。 | 平台差异天然隔离,但无法复用,需各自处理网络、存储与线程。 | 通过 expect/actual 机制在共享模块声明接口,各平台提供实现,兼顾复用与灵活性。 |
从表中可以看出,KMP 在代码量、维护成本和平台差异处理上都具有明显优势:共享模块将网络层、数据层和领域层的核心逻辑收敛到一处,既避免了双端重复开发,又通过 expect/actual 保留了平台定制能力,是跨端业务逻辑共享的高效方案。
4. 服务端开发:KMP 与 Kotlin 后端
介绍使用 Kotlin 编写服务端逻辑、共享模型与序列化方案,打通客户端与服务端的代码复用路径。下面以 Ktor 为例,展示如何在服务端复用 commonMain 中定义的数据模型,并对外提供 REST 接口。
// 服务端模块(如 serverMain)中搭建 Ktor 应用
import io.ktor.server.application.*
import io.ktor.server.engine.*
import io.ktor.server.netty.*
import io.ktor.server.routing.*
import io.ktor.server.response.*
import io.ktor.serialization.kotlinx.json.*
import io.ktor.server.plugins.contentnegotiation.*
import kotlinx.serialization.json.Json
// 直接复用 commonMain 中定义的数据模型,无需在服务端重复声明
// 例如:@Serializable data class User(val id: Int, val name: String)
// 该模型同时被 Android、iOS 客户端与服务端共享,保证字段与序列化格式完全一致
fun main() {
// 启动函数:创建并启动 Netty 引擎的 Ktor 服务
embeddedServer(Netty, port = 8080, host = "0.0.0.0") {
module()
}.start(wait = true)
}
fun Application.module() {
// 配置 JSON 序列化:与客户端保持相同的 Json 配置,确保字段解析一致
install(ContentNegotiation) {
json(Json {
ignoreUnknownKeys = true
prettyPrint = true
})
}
// 路由定义:复用共享模型作为响应体,客户端可直接反序列化
routing {
get("/users/{id}") {
val id = call.parameters["id"]?.toIntOrNull() ?: 0
// 这里直接使用 commonMain 中的 User 模型构造响应
call.respond(User(id = id, name = "KMP 全栈用户"))
}
get("/health") {
call.respond(mapOf("status" to "ok"))
}
}
}
上述示例中,服务端通过 Ktor 直接复用 commonMain 中的 User 数据模型,客户端与服务端共享同一份序列化定义,字段变更时两端无需分别维护。路由、序列化配置与启动函数均集中在服务端模块,而数据模型与业务逻辑仍沉淀在共享模块,从而实现真正的全栈代码复用。
5. 接入 AI Agent:KMP 与 LLM 应用
探讨如何在 KMP 工程中集成大模型调用、提示词管理、工具调用与流式响应,构建 AI Agent 能力。
6. 实战案例:一个跨端 AI 助手
通过一个具体案例串联 Android、服务端与 AI Agent,展示 KMP 全栈开发的完整落地流程。下面我们以「跨端 AI 助手」为例,从数据模型定义、服务端接口、双端调用到流式回复展示,逐步拆解实现步骤。
6.1 定义共享数据模型
AI 助手涉及请求、响应与消息记录三类核心数据。我们将它们统一放在 commonMain 中,借助 kotlinx.serialization 实现跨端序列化,保证 Android、iOS 与服务端使用同一份定义。
// commonMain 中定义 AI 助手的数据模型
package com.example.aiassistant.model
import kotlinx.serialization.Serializable
// 单条对话消息
@Serializable
data class ChatMessage(
val role: String, // "user" 或 "assistant"
val content: String
)
// 客户端发送给服务端的对话请求
@Serializable
data class ChatRequest(
val messages: List<ChatMessage>,
val stream: Boolean = true
)
// 服务端返回的对话响应(非流式场景)
@Serializable
data class ChatResponse(
val reply: String,
val messageId: String? = null
)
这三个模型被所有端共享:客户端用它构造请求、解析响应,服务端用它接收参数、返回结果。字段一旦变更,只需修改 commonMain 一处,双端与服务端自动同步,避免重复维护。
6.2 服务端复用模型提供对话接口
服务端模块直接复用 commonMain 中的 ChatRequest 与 ChatResponse,通过 Ktor 对外暴露一个 POST 接口。服务端收到请求后调用大模型,并把结果封装成共享模型返回。
// serverMain 中搭建对话接口
import io.ktor.server.application.*
import io.ktor.server.engine.*
import io.ktor.server.netty.*
import io.ktor.server.routing.*
import io.ktor.server.request.*
import io.ktor.server.response.*
import io.ktor.serialization.kotlinx.json.*
import io.ktor.server.plugins.contentnegotiation.*
import kotlinx.serialization.json.Json
import com.example.aiassistant.model.ChatRequest
import com.example.aiassistant.model.ChatResponse
fun main() {
embeddedServer(Netty, port = 8080, host = "0.0.0.0") {
module()
}.start(wait = true)
}
fun Application.module() {
install(ContentNegotiation) {
json(Json { ignoreUnknownKeys = true })
}
routing {
// 对话接口:复用 commonMain 中的 ChatRequest / ChatResponse
post("/api/chat") {
val request = call.receive<ChatRequest>()
// 这里调用大模型(如 OpenAI、Claude 等),此处以简化逻辑示意
val reply = "你好,我是 AI 助手。你刚才说:${request.messages.lastOrNull()?.content}"
call.respond(ChatResponse(reply = reply))
}
}
}
由于请求与响应模型都来自 commonMain,服务端无需重复声明字段,客户端与服务端的序列化格式天然一致,接口联调成本大幅降低。
6.3 Android 客户端调用接口并展示流式回复
Android 端复用共享模块中的 ChatApi 与数据模型,通过 Ktor 客户端发起请求。为展示流式回复,我们使用 SSE(Server-Sent Events)逐行读取服务端推送的内容,并实时更新界面。
// androidMain 中调用对话接口并展示流式回复
import androidx.compose.runtime.*
import com.example.aiassistant.model.ChatMessage
import com.example.aiassistant.model.ChatRequest
import io.ktor.client.*
import io.ktor.client.request.*
import io.ktor.client.statement.*
import io.ktor.http.*
// 共享模块中定义的接口
interface ChatApi {
suspend fun sendMessage(messages: List<ChatMessage>): String
}
// Android 端实现:使用 Ktor 发起请求,按行读取流式响应
class AndroidChatApi(private val client: HttpClient) : ChatApi {
override suspend fun sendMessage(messages: List<ChatMessage>): String {
val request = ChatRequest(messages = messages, stream = true)
val response = client.post("http://10.0.2.2:8080/api/chat") {
contentType(ContentType.Application.Json)
setBody(request)
}
// 简化处理:这里直接返回 body 文本,实际可按行解析 SSE 事件
return response.bodyAsText()
}
}
// Compose 界面中维护消息列表并实时追加流式内容
@Composable
fun ChatScreen(chatApi: ChatApi) {
var messages by remember { mutableStateOf(listOf<ChatMessage>()) }
var input by remember { mutableStateOf("") }
// 发送消息并展示流式回复
fun send() {
val userMsg = ChatMessage(role = "user", content = input)
messages = messages + userMsg
// 启动协程调用接口,将返回内容追加为 assistant 消息
// (实际项目中可结合 Flow 逐段更新 UI)
}
// 界面渲染逻辑省略
}
Android 端通过 AndroidChatApi 复用共享模型与接口定义,界面层只负责展示。流式回复可结合协程 Flow 将服务端推送的每个片段逐段追加到消息列表,实现打字机效果。
6.4 iOS 客户端调用接口并展示流式回复
iOS 端同样复用 commonMain 中的模型与接口,通过 Ktor 的 Darwin 引擎发起请求。SwiftUI 界面通过 ObservableObject 监听消息状态,收到流式片段后实时刷新列表。
// iosMain 中调用对话接口
import io.ktor.client.*
import io.ktor.client.request.*
import io.ktor.client.statement.*
import io.ktor.http.*
import com.example.aiassistant.model.ChatMessage
import com.example.aiassistant.model.ChatRequest
// iOS 端实现:使用 Darwin 引擎的 HttpClient
class IosChatApi(private val client: HttpClient) : ChatApi {
override suspend fun sendMessage(messages: List<ChatMessage>): String {
val request = ChatRequest(messages = messages, stream = true)
val response = client.post("http://localhost:8080/api/chat") {
contentType(ContentType.Application.Json)
setBody(request)
}
return response.bodyAsText()
}
}
// SwiftUI 侧通过桥接层调用 Kotlin 的 IosChatApi
// 收到流式片段后,使用 @Published 属性驱动列表刷新
iOS 端与 Android 端共享同一套 ChatApi 接口与数据模型,仅引擎实现不同。业务逻辑、序列化与接口契约全部沉淀在 commonMain,双端行为保持一致。
6.5 整体调用流程
下图展示了跨端 AI 助手的完整调用链路:双端客户端复用共享模型构造请求,服务端接收后调用大模型,再以流式方式把回复推回客户端实时展示。
flowchart LR
A[Android App] -->|ChatRequest| B[Ktor 服务端]
C[iOS App] -->|ChatRequest| B
B -->|调用大模型| D[LLM / AI Agent]
D -->|流式回复| B
B -->|ChatResponse 流式推送| A
B -->|ChatResponse 流式推送| C
A -->|复用 commonMain 模型| E[commonMain]
C -->|复用 commonMain 模型| E
通过上述步骤,一个跨端 AI 助手便完成了从数据模型、服务端接口到双端流式展示的完整闭环。所有核心代码集中在 commonMain,服务端与客户端共享同一份定义,真正体现了 KMP 全栈开发的价值。
7. 总结与展望
通过前文从工程架构、跨端业务共享、服务端开发到 AI Agent 接入的完整梳理,以及「跨端 AI 助手」这一实战案例的落地,KMP 全栈开发的价值与挑战已经清晰呈现。下面从收益、挑战与未来演进三个维度进行总结。
7.1 关键收益
结合前文案例,KMP 全栈开发在代码复用、开发效率与团队协作三方面带来了显著收益:
- 代码复用:正如 6.1 节所示,
ChatMessage、ChatRequest与ChatResponse三个数据模型统一沉淀在 commonMain,Android、iOS 与服务端共享同一份定义。字段一旦变更,只需修改一处即可同步三端,彻底消除了双端与服务端重复维护模型的问题。第 3 节中网络层、数据层与领域层全部收敛到共享模块,也印证了业务逻辑只写一次、多端复用的核心价值。 - 开发效率:6.2 至 6.4 节展示了服务端与双端客户端如何复用同一套
ChatApi接口与模型。服务端无需重复声明字段,客户端与服务端序列化格式天然一致,接口联调成本大幅降低。第 4 节中服务端直接复用User模型构造响应,进一步说明全栈复用能显著减少重复编码与联调时间,让团队把精力集中在业务本身。 - 团队协作:共享模块成为团队统一的「契约层」,客户端与服务端开发者围绕同一份模型与接口协作,减少了跨端沟通成本。6.5 节的整体调用流程表明,双端行为因共享逻辑而天然一致,测试也只需在共享模块中编写一次,降低了多端回归验证的负担,让团队协作更加顺畅。
7.2 当前面临的挑战
尽管收益明显,KMP 全栈开发在落地过程中仍面临以下挑战:
- 学习曲线:开发者需要同时掌握 Kotlin 多平台工程结构、expect/actual 机制、各平台引擎差异(如 Android 与 Darwin 引擎的配置)以及服务端框架(如 Ktor),上手门槛相对较高。第 3 节中
createHttpClient()的跨平台实现就要求开发者理解多平台构建与平台差异处理。 - 生态成熟度:部分第三方库对 KMP 的支持仍在完善中,某些平台特定能力(如 iOS 的 CoreData 深度集成)仍需通过 expect/actual 自行封装,生态相比纯原生方案仍有差距。第 3 节表格中数据层需按平台接入数据库驱动,正体现了这一现状。
- 构建复杂度:多平台工程的构建配置、依赖管理与平台目标(Android、iOS、服务端)的协调较为复杂,编译时间与调试成本也高于单一平台项目。第 2 节架构图中多模块的依赖注入与边界划分,需要团队投入额外精力维护。
7.3 展望:AI Agent 场景下的演进方向
随着大模型与 AI Agent 的普及,KMP 全栈开发将迎来新的演进机遇,结合前文案例可预见以下方向:
- 共享 AI 编排逻辑:提示词管理、工具调用与上下文组装等 AI 编排逻辑可像
ChatApi一样沉淀在 commonMain,双端与服务端复用同一套 Agent 行为,确保多端体验一致,同时降低重复实现成本。 - 端侧智能与隐私计算:借助 KMP 共享模型,可在客户端本地运行轻量模型或执行敏感数据预处理,减少与服务端的交互,兼顾实时性与隐私保护,这与 6.3 节流式回复的本地展示能力相辅相成。
- Agent 服务化与全栈闭环:服务端可复用共享模型将 Agent 能力封装为标准化接口,客户端通过 Ktor 统一调用,形成「共享模型 + 服务端 Agent + 双端交互」的全栈闭环,进一步放大 KMP 在 AI 驱动应用中的复用价值。
总体而言,KMP 全栈开发已在代码复用、开发效率与团队协作上展现出切实收益,虽面临学习曲线、生态与构建复杂度等挑战,但在 AI Agent 场景下,其共享逻辑与全栈复用的优势将愈发凸显,值得团队持续投入与探索。
更多推荐




所有评论(0)