1. KMP网络与数据层工程导论
在 Kotlin Multiplatform(KMP)项目中,网络与数据层并不是“把 Android 的 Retrofit 和 Room 搬到公共代码里”这么简单。它位于整个跨平台架构的中段:向上为 Compose Multiplatform、SwiftUI、ArkUI 等各平台 UI 提供统一的数据接口,向下屏蔽 OkHttp、NSURLSession、平台数据库、文件系统与安全存储的差异。理解这一层的定位,是后续所有工程决策的起点。
一、网络与数据层在 KMP 架构中的位置
一个典型的 KMP 工程通常划分为三层:表现层(各平台 UI 与 ViewModel)、领域层(纯 Kotlin 的 UseCase、实体与业务规则)、数据层(网络、缓存、持久化、同步)。数据层再细分为远程数据源(Remote DataSource)、本地数据源(Local DataSource)和仓库(Repository)。Repository 是唯一对领域层暴露的入口,它决定“先读缓存还是先请求网络”“失败时是否回退到本地”“写入后如何触发同步”。
网络与数据层的核心职责可以归纳为四点:统一数据契约(DTO、实体、错误模型在 commonMain 中定义)、平台能力抽象(HTTP 引擎、数据库驱动、键值存储通过 expect/actual 或依赖注入提供)、数据流编排(缓存策略、并发控制、重试与退避)、状态可观测(以 Flow 暴露加载、成功、失败、空数据等状态)。这四点决定了上层 UI 能否用同一套逻辑运行在 Android、iOS、Desktop 甚至 Web 上。
二、核心职责:从“能跑”到“可维护”
很多团队在 KMP 初期会把网络请求直接写在 ViewModel 里,结果 iOS 端复用困难、错误处理各写一套。正确的做法是让数据层承担以下职责:
- 统一数据契约:DTO 使用
@Serializable标注,字段命名与后端 JSON 对齐;领域实体在领域层定义,Repository 负责 DTO 到 Entity 的映射。这样后端字段变更只影响映射层,不会穿透到 UI。 - 平台能力抽象:HTTP 引擎通过
expect fun createHttpClient(): HttpClient声明,Android 用 OkHttp,iOS 用 Darwin,Desktop 用 CIO。数据库驱动同理,SQLDelight 为每个平台生成不同的 Driver。 - 数据流编排:Repository 返回
Flow<Resource<T>>,其中Resource封装 Loading、Success、Error 三种状态。UI 只订阅这个 Flow,不关心数据来自网络还是缓存。 - 可观测与可测试:数据层不依赖 Android Context 或 iOS UIViewController,可以在 commonTest 中用 MockEngine 和内存数据库完成单元测试。
三、技术选型全景
KMP 网络与数据层的选型需要同时考虑“跨平台一致性”和“平台性能”。下表给出一组常见组合及其适用场景,参数为示例性建议,实际项目需根据团队规模与业务复杂度调整。
| 层次 | 推荐方案 | 备选方案 | 关键考量 |
|---|---|---|---|
| HTTP 客户端 | Ktor Client | OkHttp + 平台封装 | Ktor 在 commonMain 中统一 API,拦截器、序列化、超时配置一次编写多端复用 |
| 序列化 | kotlinx.serialization | Moshi / Gson(仅 Android) | 编译期生成序列化器,无反射,适合 Native 与 JS |
| 本地数据库 | SQLDelight | Room KMP / Realm | SQLDelight 从 .sq 文件生成类型安全 Kotlin API,支持多平台 Driver |
| 键值存储 | Multiplatform Settings | DataStore(Android)/ NSUserDefaults(iOS) | 轻量配置、Token、开关等小数据优先用键值存储 |
| 依赖注入 | Koin | 手动构造 / kotlin-inject | Koin 在 KMP 中配置简单,适合中小团队快速落地 |
| 异步与流 | Coroutines + Flow | RxJava(不推荐新项目) | Flow 与 Compose、SwiftUI 的桥接方案成熟 |
四、学习路线图
本课程后续章节将沿着“契约 → 实现 → 优化 → 验证”的路径展开。建议按以下顺序推进:
- 基础契约:先定义 DTO、Entity、Resource 与错误模型,确保 commonMain 中不出现平台类型。
- 网络实现:用 Ktor Client 搭建请求管线,配置 ContentNegotiation、Logging、Auth 拦截器与超时。
- 本地持久化:引入 SQLDelight,设计表结构与查询,处理事务、迁移与并发读写。
- 仓库编排:实现“网络优先 + 缓存回退”“缓存优先 + 后台刷新”等策略,用 Flow 暴露状态。
- 同步与冲突:处理离线写入、增量同步、版本冲突与幂等重试。
- 测试与观测:用 MockEngine、内存数据库和 Turbine 验证数据流,接入日志与性能指标。
Context、iOS 的 NSURLSession 回调)直接写进 commonMain。这会导致 iOS 编译失败或行为不一致。正确做法是通过 expect/actual 或依赖注入在平台侧提供实现,commonMain 只依赖接口。
数据层的质量直接决定 KMP 项目的可维护性。一个清晰的 Repository 边界、一套统一的错误模型、一条可观测的数据流,能让后续的 UI 开发、测试和排障都变得可控。接下来的章节将从最基础的 DTO 与 Entity 设计开始,逐步构建完整的网络与数据层。