# KMPWanDemo **Repository Path**: jackning_admin/kmpwan-demo ## Basic Information - **Project Name**: KMPWanDemo - **Description**: No description available - **Primary Language**: Kotlin - **License**: Apache-2.0 - **Default Branch**: master - **Homepage**: None - **GVP Project**: No ## Statistics - **Stars**: 0 - **Forks**: 0 - **Created**: 2026-09-08 - **Last Updated**: 2026-09-19 ## Categories & Tags **Categories**: Uncategorized **Tags**: None ## README # KMPWanDemo # 介绍 基于 **Kotlin Multiplatform + Compose Multiplatform (M3) + Ktor + MVI 架构** 实现跨平台玩 Android文章列表Demo,一套代码同时运行在 **Android、JVM 桌面、iOS、JS、WasmJs** 五个平台。 采用分层架构:`data数据实体`‑`net多平台网络工厂`‑`http仓库层Repository`‑`viewmodel(MVI)`‑`ui界面层`;利用`expect‑actual`完成平台差异化实现;`compose‑resources`统一管理字体资源,壳子模块只做入口,不掺杂业务逻辑。 ## 核心实现要点 1. **多平台 Ktor 网络** 通过`expect fun createKtorHttpClient():HttpClient`做声明,各平台源集提供对应 actual 引擎实现: - Android:`ktor‑client‑okhttp` - JVM 桌面:`ktor‑client‑java`(规避 CIO 引擎 TLS 握手兼容问题) - iOS:`ktor‑client‑darwin` - JS/WasmJs:`ktor‑client‑js` / `ktor‑client‑wasm` 2.**序列化** 引入`kotlin‑plugin‑serialization`插件,使用`@Serializable`注解定义实体,Ktor 安装`ContentNegotiation`完成 JSON 解析。 3.**MVI 单向数据流** - `sealed class ArticleIntent`:定义用户行为(刷新) - `sealed class ArticleUiState`:统一页面状态:Loading / Success / Error - ViewModel 持有 Repository,`viewModelScope`执行协程网络;UI 层`collectAsStateWithLifecycle`监听状态渲染页面。 4.**跨平台字体统一** 把 NotoSansSC 中文字体放入`commonMain/composeResources`,封装`rememberCustomTypography()`对外暴露,**所有壳模块 (Android / 桌面 / Web) 统一使用该 Typography,解决 WasmJs 网页中文乱码**,壳模块禁止直接访问`Res`,规避 internal 访问报错。 ## 踩坑清单 & 解决方案 | 问题 | 现象 | 解决方案 | | ----------------------------- | ------------------------------- | ------------------------------------------------------------ | | 1. 序列化失败 | @Serializable 不生效 | toml 配置`kotlinSerialization`插件,shared 模块应用该插件 | | 2.JVM 桌面网络 HTTPS 握手失败 | CIO 引擎 TLS 兼容性差 | jvmMain 使用`ktor‑client‑java`引擎,放弃 cio | | 3.WasmJs 浏览器 CORS 跨域 | 浏览器拦截 wanandroid 接口请求 | 两种方案:①webpack devServer 代理;②独立 node 代理服务器转发接口请求,访问代理地址 | | 4.WasmJs 中文乱码 | 网页端中文显示方框乱码 | common 层引入中文字体,封装 Typography,所有平台统一应用该字体 | | 5.expect‑actual 编译报错 | `no actual declaration for JVM` | **每个 expect,所有 target 平台必须补齐 actual 实现,极易漏掉 jvmMain 桌面端** | | 6.Ktor 引擎导包错误 | 各平台使用错误 http 引擎 | 各个 sourceSets 分别引入对应平台 ktor 客户端依赖,commonMain 只引入`ktor‑client‑core`核心包 | This is a Kotlin Multiplatform project targeting Android, iOS, Web, Desktop (JVM). * [/iosApp](./iosApp/iosApp) contains an iOS application. Even if you’re sharing your UI with Compose Multiplatform, you need this entry point for your iOS app. This is also where you should add SwiftUI code for your project. * [/shared](./shared/src) is for code that will be shared across your Compose Multiplatform applications. It contains several subfolders: - [commonMain](./shared/src/commonMain/kotlin) is for code that’s common for all targets. - Other folders are for Kotlin code that will be compiled for only the platform indicated in the folder name. For example, if you want to use Apple’s CoreCrypto for the iOS part of your Kotlin app, the [iosMain](./shared/src/iosMain/kotlin) folder would be the right place for such calls. Similarly, if you want to edit the Desktop (JVM) specific part, the [jvmMain](./shared/src/jvmMain/kotlin) folder is the appropriate location. ### Running the apps Use the run configurations provided by the run widget in your IDE's toolbar. You can also use these commands and options: - Android app: `./gradlew :androidApp:assembleDebug` - Desktop app: - Hot reload: `./gradlew :desktopApp:hotRun --auto` - Standard run: `./gradlew :desktopApp:run` - Web app: - Wasm target (faster, modern browsers): `./gradlew :webApp:wasmJsBrowserDevelopmentRun` - JS target (slower, supports older browsers): `./gradlew :webApp:jsBrowserDevelopmentRun` - iOS app: open the [/iosApp](./iosApp) directory in Xcode and run it from there. ### Running tests Use the run button in your IDE's editor gutter, or run tests using Gradle tasks: - Android tests: `./gradlew :shared:testAndroidHostTest` - Desktop tests: `./gradlew :shared:jvmTest` - Web tests: - Wasm target: `./gradlew :shared:wasmJsTest` - JS target: `./gradlew :shared:jsTest` - iOS tests: `./gradlew :shared:iosSimulatorArm64Test` --- ################## 目录结构说明 ################## ``` KMPWanDemo/ ├─ androidApp/ # Android壳模块,只负责Activity,无业务 ├─ desktopApp/ # JVM桌面壳,只负责窗口入口 ├─ iosApp/ # iOS壳,ComposeUIViewController入口 ├─ webApp/ # WasmJs/JS网页壳,ComposeViewport入口 └─ shared/ # 核心跨平台业务模块 └─ src ├─ commonMain │ ├─ composeResources/ # compose‑resources资源(字体、图片) │ │ └─ font/ │ └─ kotlin/com/example/kmpwandemo │ ├─ data # 数据模型层(序列化实体) │ │ ├─ Article.kt │ │ └─ ArticleListResponse.kt │ ├─ net # 网络工厂、expect声明、日志工具 │ │ ├─ HttpClientFactory.kt // expect fun createKtorHttpClient() │ │ └─ AppLogger.kt // expect日志封装 │ ├─ http # Repository业务仓库层 │ │ └─ WanRepository.kt │ ├─ ui # Compose UI全部页面组件 │ │ ├─ screen │ │ │ └─ ArticleListScreen.kt │ │ └─ component # 可复用小组件 │ │ └─ ArticleItem.kt │ ├─ viewmodel # MVI ViewModel │ │ └─ ArticleListViewModel.kt │ ├─ theme # 主题、字体、Typography │ │ ├─ AppFonts.kt │ │ └─ FontProvider.kt │ ├─ App.kt # 根Compose组件 │ └─ Platform.kt // expect fun getWanBaseUrl() │ ├─ androidMain/kotlin/com/example/kmpwandemo │ ├─ net │ │ ├─ HttpClientFactory.android.kt // actual 客户端 okhttp │ │ └─ AppLogger.android.kt │ └─ Platform.android.kt // actual getWanBaseUrl │ ├─ jvmMain/kotlin/com/example/kmpwandemo │ ├─ net │ │ ├─ HttpClientFactory.jvm.kt // actual 客户端 java │ │ └─ AppLogger.jvm.kt │ └─ Platform.jvm.kt // actual getWanBaseUrl【桌面】 │ ├─ iosMain/kotlin/com/example/kmpwandemo │ ├─ net │ │ ├─ HttpClientFactory.ios.kt │ │ └─ AppLogger.ios.kt │ └─ Platform.ios.kt │ ├─ jsMain/kotlin/com/example/kmpwandemo │ ├─ net │ │ ├─ HttpClientFactory.js.kt │ │ └─ AppLogger.js.kt │ └─ Platform.js.kt │ └─ wasmJsMain/kotlin/com/example/kmpwandemo ├─ net │ ├─ HttpClientFactory.wasm.kt │ └─ AppLogger.wasm.kt └─ Platform.wasm.kt ``` ################## 目录结构说明 ################## Learn more about [Kotlin Multiplatform](https://www.jetbrains.com/help/kotlin-multiplatform-dev/get-started.html), [Compose Multiplatform](https://kotlinlang.org/compose-multiplatform/), [Kotlin/Wasm](https://kotl.in/wasm/)… We would appreciate your feedback on Compose/Web and Kotlin/Wasm in the public Slack channel [#compose-web](https://slack-chats.kotlinlang.org/c/compose-web). If you face any issues, please report them on [YouTrack](https://youtrack.jetbrains.com/newIssue?project=CMP).