一、 引言:KMP的崛起与全栈愿景

Kotlin Multiplatform (KMP) 作为 JetBrains 推出的跨平台解决方案,正从移动端(Android/iOS)向更广阔的全栈领域演进。本文将探讨如何利用 KMP 构建从移动端到服务端,乃至新兴 AI Agent 应用的全栈技术体系。

二、 KMP 技术核心与生态现状

  • 共享业务逻辑: 使用 Kotlin 编写一次,在 JVM、Native、JS 等多平台运行。
  • 平台特定实现: 通过 expect/actual 机制处理平台差异。
  • 成熟度与工具链: Compose Multiplatform、Ktor、SQLDelight 等生态工具。

三、 第一站:巩固 Android 与 iOS 移动端

  • 架构模式: 在共享模块中实现 MVVM/MVI 的 ViewModel 与 UseCase。
// 共享模块中的 ViewModel 示例
class TaskViewModel(private val taskRepository: TaskRepository) : ViewModel() {
    private val _tasks = MutableStateFlow<List<Task>>(emptyList())
    val tasks: StateFlow<List<Task>> = _tasks.asStateFlow()
    
    fun loadTasks() {
        viewModelScope.launch {
            _tasks.value = taskRepository.getAllTasks()
        }
    }
    
    fun addTask(title: String) {
        viewModelScope.launch {
            taskRepository.addTask(Task(title = title))
            loadTasks() // 重新加载列表
        }
    }
}
  • UI 共享实践: Compose Multiplatform 实现跨平台 UI。
// Compose Multiplatform 共享 UI 组件
@Composable
fun TaskListScreen(
    tasks: List<Task>,
    onTaskClick: (Task) -> Unit,
    onAddTask: () -> Unit
) {
    Column(
        modifier = Modifier.fillMaxSize().padding(16.dp)
    ) {
        Text(
            text = "任务列表",
            style = MaterialTheme.typography.h5,
            modifier = Modifier.padding(bottom = 16.dp)
        )
        
        LazyColumn {
            items(tasks) { task ->
                TaskItem(
                    task = task,
                    onClick = { onTaskClick(task) }
                )
            }
        }
        
        FloatingActionButton(
            onClick = onAddTask,
            modifier = Modifier.align(Alignment.End)
        ) {
            Icon(Icons.Default.Add, contentDescription = "添加任务")
        }
    }
}
  • 状态与导航管理: 使用 Voyager 或 Decompose 进行跨平台状态与导航管理。
// 使用 expect/actual 处理平台差异的导航示例
// 共享模块中的 expect 声明
expect class PlatformNavigator() {
    fun navigateTo(screen: Screen)
    fun goBack()
}

// Android 平台的 actual 实现
actual class PlatformNavigator actual constructor() {
    private val navController = rememberNavController()
    
    actual fun navigateTo(screen: Screen) {
        navController.navigate(screen.route)
    }
    
    actual fun goBack() {
        navController.popBackStack()
    }
}

// iOS 平台的 actual 实现(在 iOS 源集中)
// actual class PlatformNavigator actual constructor() {
//     // 使用 UIKit 或 SwiftUI 的导航实现
// }

四、 迈向全栈:KMP 在后端与 Web 前端的应用

  • Ktor 构建后端 API: 共享数据模型、验证逻辑与业务规则。
// Ktor 后端 API 路由定义示例
fun Application.configureRouting() {
    routing {
        route("/api/tasks") {
            get {
                val tasks = taskService.getAllTasks()
                call.respond(HttpStatusCode.OK, tasks)
            }
            
            post {
                val taskRequest = call.receive<TaskRequest>()
                val createdTask = taskService.createTask(taskRequest)
                call.respond(HttpStatusCode.Created, createdTask)
            }
            
            route("/{id}") {
                get {
                    val id = call.parameters["id"]?.toIntOrNull()
                    ?: throw BadRequestException("Invalid ID")
                    
                    val task = taskService.getTaskById(id)
                    call.respond(HttpStatusCode.OK, task)
                }
                
                put {
                    val id = call.parameters["id"]?.toIntOrNull()
                    ?: throw BadRequestException("Invalid ID")
                    val taskRequest = call.receive<TaskRequest>()
                    
                    val updatedTask = taskService.updateTask(id, taskRequest)
                    call.respond(HttpStatusCode.OK, updatedTask)
                }
                
                delete {
                    val id = call.parameters["id"]?.toIntOrNull()
                    ?: throw BadRequestException("Invalid ID")
                    
                    taskService.deleteTask(id)
                    call.respond(HttpStatusCode.NoContent)
                }
            }
        }
    }
}
  • Kotlin/JS 与 React: 共享逻辑在 Web 前端的落地。
// Kotlin/JS 与 React 集成示例
// 共享的业务逻辑
class TaskManager(val apiClient: ApiClient) {
    suspend fun fetchTasks(): List<Task> {
        return apiClient.get("/api/tasks")
    }
    
    suspend fun createTask(title: String): Task {
        return apiClient.post("/api/tasks", TaskRequest(title = title))
    }
}

// React 组件使用共享逻辑
@JsExport
class TaskListComponent(private val taskManager: TaskManager) {
    private var tasks: Array<dynamic> = emptyArray()
    
    @JsExport.Ignore
    suspend fun loadTasks() {
        tasks = taskManager.fetchTasks().toTypedArray()
    }
    
    @JsExport
    fun render(): dynamic {
        return React.createElement("div", null,
            React.createElement("h1", null, "任务列表"),
            React.createElement("ul", null,
                tasks.map { task ->
                    React.createElement("li", { key: task.id }, task.title)
                }
            )
        )
    }
}
  • 数据库与缓存: SQLDelight 实现多平台一致的数据库访问。
// SQLDelight 数据库操作示例
// 共享模块中的数据库定义
class TaskDatabase(driver: SqlDriver) {
    private val database = TaskDb(driver)
    private val queries = database.taskDbQueries
    
    fun getAllTasks(): List<Task> {
        return queries.selectAll().executeAsList().map { it.toTask() }
    }
    
    fun insertTask(task: Task) {
        queries.insertTask(
            id = task.id,
            title = task.title,
            completed = task.completed,
            created_at = task.createdAt
        )
    }
    
    fun updateTask(task: Task) {
        queries.updateTask(
            title = task.title,
            completed = task.completed,
            id = task.id
        )
    }
    
    fun deleteTask(id: Long) {
        queries.deleteTask(id)
    }
}

// 平台特定的驱动创建
expect fun createSqlDriver(): SqlDriver

// Android 实现
actual fun createSqlDriver(): SqlDriver {
    return AndroidSqliteDriver(TaskDb.Schema, context, "tasks.db")
}

// iOS 实现(在 iOS 源集中)
// actual fun createSqlDriver(): SqlDriver {
//     return NativeSqliteDriver(TaskDb.Schema, "tasks.db")
// }

五、 新边疆:KMP 在 AI Agent 开发中的潜力

  • AI Agent 架构概述: 感知、规划、执行与学习循环。
// AI Agent 基础架构示例
interface AIAgent {
    suspend fun perceive(context: AgentContext): Perception
    suspend fun plan(perception: Perception): Plan
    suspend fun execute(plan: Plan): ActionResult
    suspend fun learn(result: ActionResult): LearningOutcome
}

class SimpleAIAgent : AIAgent {
    private val memory = mutableListOf<MemoryEntry>()
    private val tools = listOf<Tool>(WebSearchTool(), CalculatorTool(), FileSystemTool())
    
    override suspend fun perceive(context: AgentContext): Perception {
        return Perception(
            userInput = context.userInput,
            currentState = context.state,
            availableTools = tools,
            memory = memory.takeLast(10) // 最近10条记忆
        )
    }
    
    override suspend fun plan(perception: Perception): Plan {
        // 基于感知结果制定计划
        val toolToUse = selectTool(perception)
        return Plan(
            steps = listOf(
                Step("使用工具: ${toolToUse.name}", toolToUse),
                Step("分析结果", AnalysisAction()),
                Step("生成响应", ResponseAction())
            )
        )
    }
    
    override suspend fun execute(plan: Plan): ActionResult {
        // 执行计划中的每个步骤
        val results = mutableListOf<StepResult>()
        for (step in plan.steps) {
            val result = step.action.execute(step.context)
            results.add(result)
        }
        return ActionResult(results)
    }
    
    override suspend fun learn(result: ActionResult): LearningOutcome {
        // 从执行结果中学习
        memory.add(MemoryEntry(result, System.currentTimeMillis()))
        return LearningOutcome(improved = result.successful)
    }
    
    private fun selectTool(perception: Perception): Tool {
        // 简单的工具选择逻辑
        return perception.availableTools.firstOrNull { it.canHandle(perception.userInput) }
            ?: perception.availableTools.first()
    }
}
  • KMP 作为 Agent 的“大脑”: 共享推理逻辑、工具调用封装与记忆管理。
// 共享的 Agent 推理逻辑
class SharedReasoningEngine {
    private val knowledgeBase = KnowledgeBase()
    private val reasoningRules = listOf<ReasoningRule>()
    
    suspend fun reasonAbout(context: ReasoningContext): ReasoningResult {
        return coroutineScope {
            // 并行执行多个推理策略
            val strategyResults = listOf(
                async { deductiveReasoning(context) },
                async { inductiveReasoning(context) },
                async { abductiveReasoning(context) }
            ).awaitAll()
            
            // 综合各策略结果
            combineResults(strategyResults)
        }
    }
    
    suspend fun planNextAction(
        currentState: AgentState,
        goal: Goal
    ): ActionPlan {
        val possibleActions = generatePossibleActions(currentState, goal)
        val scoredActions = possibleActions.map { action ->
            val score = evaluateAction(action, currentState, goal)
            ScoredAction(action, score)
        }
        
        return ActionPlan(
            bestAction = scoredActions.maxByOrNull { it.score }?.action,
            alternativeActions = scoredActions.sortedByDescending { it.score }
        )
    }
    
    private suspend fun deductiveReasoning(context: ReasoningContext): DeductiveResult {
        // 演绎推理实现
        return DeductiveResult(conclusions = emptyList())
    }
    
    private suspend fun inductiveReasoning(context: ReasoningContext): InductiveResult {
        // 归纳推理实现
        return InductiveResult(generalizations = emptyList())
    }
    
    private suspend fun abductiveReasoning(context: ReasoningContext): AbductiveResult {
        // 溯因推理实现
        return AbductiveResult(explanations = emptyList())
    }
    
    private fun combineResults(results: List<ReasoningResult>): ReasoningResult {
        // 综合推理结果
        return ReasoningResult(confidence = results.map { it.confidence }.average())
    }
}
  • 多平台交互界面: 为同一 AI Agent 提供 App、Web、Cli 等多种交互前端。
// 共享的 Agent 交互接口
interface AgentUI {
    suspend fun displayMessage(message: AgentMessage)
    suspend fun getInput(prompt: String): String
    suspend fun showOptions(options: List<String>): Int
    suspend fun updateStatus(status: AgentStatus)
}

// Android/iOS 共享的 Compose UI
@Composable
fun AgentChatScreen(
    agent: AIAgent,
    conversation: List<ChatMessage>,
    onSendMessage: (String) -> Unit
) {
    Column(
        modifier = Modifier.fillMaxSize()
    ) {
        // 消息列表
        LazyColumn(
            modifier = Modifier.weight(1f),
            reverseLayout = true
        ) {
            items(conversation.reversed()) { message ->
                ChatBubble(message = message)
            }
        }
        
        // 输入框
        var inputText by remember { mutableStateOf("") }
        Row(
            modifier = Modifier.fillMaxWidth().padding(8.dp),
            verticalAlignment = Alignment.CenterVertically
        ) {
            TextField(
                value = inputText,
                onValueChange = { inputText = it },
                modifier = Modifier.weight(1f),
                placeholder = { Text("输入消息...") }
            )
            
            Button(
                onClick = {
                    onSendMessage(inputText)
                    inputText = ""
                },
                enabled = inputText.isNotBlank()
            ) {
                Text("发送")
            }
        }
    }
}

// Web 前端实现(Kotlin/JS)
@JsExport
class WebAgentUI : AgentUI {
    override suspend fun displayMessage(message: AgentMessage) {
        val messageDiv = document.createElement("div")
        messageDiv.className = "agent-message"
        messageDiv.textContent = message.content
        document.getElementById("chat-container")?.appendChild(messageDiv)
    }
    
    override suspend fun getInput(prompt: String): String {
        return js("prompt(prompt)") as String
    }
    
    override suspend fun showOptions(options: List<String>): Int {
        val optionText = options.joinToString("\n") { "${it.index + 1}. $it" }
        val choice = js("prompt('请选择:\\n' + optionText)") as String
        return choice.toIntOrNull()?.minus(1) ?: 0
    }
    
    override suspend fun updateStatus(status: AgentStatus) {
        document.getElementById("status")?.textContent = status.toString()
    }
}
  • 实战构想: 基于 KMP 构建跨平台的个人助理、自动化脚本 Agent 或代码生成工具。
// 跨平台代码生成 Agent 示例
class CodeGenerationAgent : AIAgent {
    private val codeGenerator: CodeGenerator
    private val codeAnalyzer: CodeAnalyzer
    private val testGenerator: TestGenerator
    
    suspend fun generateCodeFromSpec(spec: CodeSpec): GeneratedCode {
        // 1. 分析需求
        val requirements = analyzeRequirements(spec)
        
        // 2. 生成代码结构
        val structure = generateStructure(requirements)
        
        // 3. 填充实现
        val implementations = generateImplementations(structure)
        
        // 4. 生成测试
        val tests = generateTests(implementations)
        
        // 5. 生成文档
        val documentation = generateDocumentation(implementations)
        
        return GeneratedCode(
            structure = structure,
            implementations = implementations,
            tests = tests,
            documentation = documentation
        )
    }
    
    suspend fun refactorCode(
        existingCode: String,
        refactoringGoal: RefactoringGoal
    ): RefactoringResult {
        // 分析现有代码
        val analysis = codeAnalyzer.analyze(existingCode)
        
        // 制定重构计划
        val refactoringPlan = planRefactoring(analysis, refactoringGoal)
        
        // 执行重构
        val refactoredCode = executeRefactoring(existingCode, refactoringPlan)
        
        // 验证重构结果
        val validation = validateRefactoring(refactoredCode, analysis)
        
        return RefactoringResult(
            originalCode = existingCode,
            refactoredCode = refactoredCode,
            changes = refactoringPlan.changes,
            validationResult = validation
        )
    }
    
    // 平台特定的代码生成
    expect fun generatePlatformSpecificCode(
        platform: TargetPlatform,
        logic: SharedLogic
    ): PlatformCode
    
    // Android 实现
    actual fun generatePlatformSpecificCode(
        platform: TargetPlatform.Android,
        logic: SharedLogic
    ): PlatformCode {
        return generateAndroidCode(logic, platform.minSdk, platform.targetSdk)
    }
    
    // iOS 实现
    actual fun generatePlatformSpecificCode(
        platform: TargetPlatform.IOS,
        logic: SharedLogic
    ): PlatformCode {
        return generateSwiftCode(logic, platform.deploymentTarget)
    }
    
    // Web 实现
    actual fun generatePlatformSpecificCode(
        platform: TargetPlatform.Web,
        logic: SharedLogic
    ): PlatformCode {
        return generateJavaScriptCode(logic, platform.targetBrowsers)
    }
}

六、 技术挑战与最佳实践

  • 并发与协程: 跨平台协程使用的一致性与陷阱。
  • 依赖管理与二进制大小: 优化编译产物,控制包体积。
  • 测试策略: 共享测试与平台特定测试的结合。
  • 团队协作与模块化: 如何组织大型 KMP 全栈项目。

以下是 KMP 项目中常见的二进制大小优化策略对比:

策略名称适用场景预期效果
使用 cinterop 优化需要调用原生 C/C++/Objective-C 库的场景减少不必要的桥接代码,降低二进制大小 10-30%
资源压缩与优化项目包含大量图片、字体、配置文件等资源文件减少资源文件体积 20-50%,降低整体包大小
代码混淆与优化发布版本需要保护代码逻辑并减小体积移除未使用代码,优化字节码,减小 15-40%
依赖库精简依赖过多第三方库,包含大量未使用功能通过依赖分析工具移除无用依赖,减小 5-25%
编译器优化选项对性能敏感且需要最小化二进制大小的项目启用 -opt、-Xsize 等编译器标志,减小 5-15%
多平台代码分离不同平台功能差异大,共享代码比例不高按平台分离代码,避免打包无用平台代码,减小 20-60%
使用轻量级替代库当前依赖库过于臃肿,有更轻量的替代方案替换为功能相似但体积更小的库,减小 10-30%

这些策略可以单独或组合使用,根据项目具体情况选择最合适的优化方案。

七、 未来展望与学习路径

  • 技术趋势: KMP 与 Compose 的进一步融合,Wasm 支持。
  • 社区与资源: 核心学习资料、开源项目与社区动态。
  • 入门建议: 从移动端共享逻辑开始,逐步扩展到全栈与 AI 领域。

八、 结语

KMP 为实现“一次编写,多处运行”的全栈开发提供了坚实路径。从成熟的移动端到探索中的 AI Agent,KMP 生态持续进化,为开发者打开了新的可能性。

Logo

Agent 垂直技术社区,欢迎活跃、内容共建。

更多推荐