# kmp-cmp-harmony
**Repository Path**: licq_workspace/kmp-cmp-native
## Basic Information
- **Project Name**: kmp-cmp-harmony
- **Description**: 一套 Kotlin 代码跑通 Android / iOS / 鸿蒙 NEXT:KMP + Compose Multiplatform 鸿蒙统一渲染(华为 CPF 工具链)真实落地,原生壳 + CMP 混排架构,三端统一视频播放器,附完整踩坑复盘文档。
- **Primary Language**: Kotlin
- **License**: Not specified
- **Default Branch**: main
- **Homepage**: None
- **GVP Project**: No
## Statistics
- **Stars**: 1
- **Forks**: 0
- **Created**: 2026-09-10
- **Last Updated**: 2026-09-15
## Categories & Tags
**Categories**: Uncategorized
**Tags**: None
## README
# KMP · CMP · 三端同源
**一套 Kotlin 代码,Android / iOS / 鸿蒙 NEXT 三端原生体验**
One Kotlin codebase, three platforms: Android, iOS & HarmonyOS NEXT.
[](#-三端架构原生壳--cmp-内容)
[-7F52FF)](#-技术栈)
[](#-技术栈)
[](#-核心架构原生壳--cmp-内容)
---
## ✨ 这是什么
一个 **Kotlin Multiplatform + Compose Multiplatform** 的三端完整落地工程:
- 🎯 **鸿蒙 NEXT 上真实跑通的 CMP 统一渲染**——华为 CPF-KMP-CMP 工具链(Kotlin `2.2.21-1.0.0`,含 `ohosArm64` 变体),KN 编译出 `libkn.so` 经 NAPI 宿主嵌入 ArkUI,公开资料稀缺,本仓库全程真机验证
- 🏗 **原生壳 + CMP 内容页混排**:启动页/Tab 栏保原生(三端各自最优体验),业务页三端同一份 Compose 组件——不追求"100% 一套",追求**每端都像原生 App**
- 📺 **三端统一视频播放器**:一套 `XikaVideo` 声明式 API(playControl / rate / muted + 状态/进度/首帧回调),三端适配器 = ExoPlayer / AVPlayer / ArkUI Video,对齐腾讯 Kuikly Video 的接口设计
- 🧪 **真实数据源**:首页 wanAndroid 开放 API、视频 B 站解析直链 + 阿里云/iTunes 源、音乐 iTunes 试听——无假数据演示
- 📚 **完整踩坑复盘**:ComposeScene 崩溃、XComponent 原生断言、AVPlayer 状态机竞态、KN 依赖断流……每篇带现象/取证/根因/修复,都是真机趟出来的
> [!TIP]
> 如果你正在调研「鸿蒙上能不能跑 CMP」「KMP 三端怎么混排」,直接看文末 [🧭 踩坑复盘](#-踩坑复盘精选)——那几篇 POSTMORTEM 比任何教程都真实。
## 📺 效果演示
| Android(ExoPlayer) | iOS(AVPlayer) | 鸿蒙(ArkUI Video + CMP 混排) |
|:---:|:---:|:---:|
| 待补 GIF | 待补 GIF | 待补 GIF |
## 🧱 三端架构:原生壳 + CMP 内容
| 层 | 实现方 | 内容 |
|---|---|---|
| 启动页/引导页 | 各端原生 | Android `SplashActivity`(View)、iOS `SplashView/GuideView`(SwiftUI)、鸿蒙 `Splash.ets`(ArkUI) |
| 主页 Tab 壳 | 各端原生 | 底部 4 Tab 导航;Tab 配置统一取自 sharedCore `TabConfigManager`(单一事实源) |
| Tab 内容页 | **CMP(三端同一份)** | shared 模块 `XikaHomePageContent` / `VideoApp` / `MusicApp` / `MineApp` |
| 视频播放 | 各端内核 + 统一 API | `XikaVideo` → Android ExoPlayer / iOS AVPlayer / 鸿蒙 ArkUI Video 原生播放页 |
| 系统能力承接 | 各端原生 | H5 容器(WebView/WKWebView/ArkWeb)、音乐播放桥、网络引擎(OkHttp/Darwin/rcp) |
```mermaid
flowchart LR
subgraph native["三端原生壳"]
A1["Android
TabBar + ComposeView"]
A2["iOS
UITabBarController + CMP-VC"]
A3["鸿蒙
ArkUI Tabs + Compose()"]
end
subgraph shared["shared / sharedCore (KMP, 一套代码)"]
B1["CMP 内容页
home · video · music · mine (MVI)"]
B2["XikaVideo 统一播放 API"]
B3["网络(Ktor/wanAndroid) · 桥接协议 · TabConfig"]
end
subgraph kernel["平台内核"]
C1["ExoPlayer"]
C2["AVPlayer"]
C3["ArkUI Video / NAPI(libkn.so)"]
end
A1 --> B1
A2 --> B1
A3 --> B1
B1 --> B2
B1 --> B3
B2 --> C1
B2 --> C2
B2 --> C3
```
**CMP ↔ 原生交互契约(三端一致)**:`onSwitchTab(index)` 跨 Tab 跳转、`onNavigateH5(url)` H5 打开、`onOpenExternal(url)` 站外链接。
## 📦 模块结构
```
├── sharedCore/ # ★ 逻辑核心(纯 Kotlin,无 UI):网络(Ktor)、桥接协议、领域模型、DI
│ └── domain/home/ # HomeRepository(wanAndroid 实时数据) · TabConfig(Tab 单一事实源)
├── shared/ # ★ CMP 共享 UI 层(android + ios + ohosArm64)
│ └── ui/
│ ├── home/ # XikaHomePageContent(首页卡片流,MVI) · 三态(加载/失败/内容)
│ ├── video/ # 视频 Tab + XikaVideo 三端统一播放 API
│ ├── music/ # 音乐 Tab(iTunes 30s 试听)
│ ├── mine/ # 我的 Tab
│ └── common/ # RemoteImage · MiniJsonParser(轻量 JSON 解析)
├── shared-login/ # 登录独立域模块(纯逻辑,四步授权链)
├── composeApp/ # 鸿蒙 CMP 宿主:@CName 导出 4 个 ArkUIViewController → libkn.so
├── androidApp/ # Android 壳:HomeActivity(原生 TabBar + 4 个常驻 ComposeView)
├── iosApp/ # iOS 壳:TabRootView(UITabBarController + 4 个 CMP controller,进程级缓存)
├── harmonyApp/ # 完整 DevEco 工程:Home.ets(原生 Tabs 壳)+ NAPI 宿主(libentry.so)
├── build-scripts/ # 构建/验证脚本
└── docs/ # 方案与复盘文档(见文末索引)
```
## 📡 数据与能力
- **首页数据**:wanAndroid 开放 API(Banner + 文章瀑布流),纯远程、无本地 mock;失败显示失败态 + 重试
- **首页状态**:`HomeViewModel` 模块级单例 + `Init` 幂等——鸿蒙 surface 销毁重建(播放/切后台/息屏)后组合树重建时数据即时恢复,不闪加载页
- **视频**:三端统一 `XikaVideo` API(Kotlin 声明式,对齐 Kuikly Video 接口形状);源 = B 站解析直链(国内 CDN,运行时解析、签名时效约 2.5h)+ 阿里云演示片 + w3school 片段 + iTunes MV;鸿蒙端播放页内核为 ArkUI `Video` 组件(系统接管 prepare/缓冲时序)
- **音乐**:iTunes 试听(30s m4a);Android MediaPlayer / iOS AVAudioPlayer / 鸿蒙 AudioHost(AVPlayer 桥)
- **图片**:Android BitmapFactory 解码;鸿蒙/iOS 暂为首字占位(华为 CMP 构件缺 `Image.makeFromEncoded` 符号)
## 🚀 快速开始
前置环境:JDK 17+、Android SDK;鸿蒙需 DevEco Studio(含 ohos SDK/hdc);iOS 需完整 Xcode。
### Android
```bash
./gradlew :androidApp:assembleDebug
adb install -r androidApp/build/outputs/apk/debug/androidApp-debug.apk
```
### 鸿蒙(release 链接,勿用 debug 产物,见「鸿蒙填坑清单」)
```bash
# 1. 链接 libkn.so 并同步到鸿蒙工程
./gradlew :composeApp:publishReleaseBinariesToHarmonyApp
# 2. 构建 HAP(或在 DevEco Studio 中打开 harmonyApp/ 直接构建)
cd harmonyApp && DEVECO_SDK_HOME=/Applications/DevEco-Studio.app/Contents/sdk \
/Applications/DevEco-Studio.app/Contents/tools/hvigor/bin/hvigorw \
--mode module -p module=entry@default -p product=default -p buildMode=debug assembleHap --no-daemon
# 3. 安装启动
hdc install -r entry/build/default/outputs/default/entry-default-signed.hap
hdc shell aa start -a EntryAbility -b # bundleName 见 AppScope/app.json5
```
> 注意:`harmonyApp/entry/libs/`、`harmonyApp/entry/.cxx/`、`cpp/include/libkn_api.h` 为生成物,不入库;新 clone 后必须先执行第 1 步,否则 hvigor 原生编译缺符号。
### iOS(需完整 Xcode)
```bash
./gradlew :shared:linkReleaseFrameworkIosSimulatorArm64 # 或由 Xcode Run Script 自动执行
# 打开 iosApp/KmpXika.xcodeproj 构建运行
```
### 单元测试
```bash
./gradlew :sharedCore:testDebugUnitTest
```
## 🧭 踩坑复盘(精选)
> 每篇都是真机趟出来的:现象 → 被推翻的假设 → 取证过程 → 根因 → 修复 → 验证。
| 复盘 | 根因一句话 |
|---|---|
| [iOS 首启全链崩溃(5 层问题)](docs/IOS-CRASH-POSTMORTEM.md) | App 入口从未调用 `SharedGate.bootstrap()`,首页首次 watch 未注册订阅打死 ComposeScene(表象是误导性的 `ComposeScene is already closed`) |
| [鸿蒙 CMP 崩溃复盘](docs/HARMONY-CRASH-POSTMORTEM.md) | KN 双 so 并存 = 双 runtime 互踩 SIGABRT;统一渲染下 XComponent 原生断言——合并为单一 libkn.so 是唯一正解 |
| AVPlayer 状态机竞态(鸿蒙 v3) | `surfaceId/prepare/play` 必须严格按 `initialized → prepared` 状态回调时序,同步连调必炸 5400102 |
| Gitee 拉取/KN 依赖断流 | CPF 工具链构件在华为 maven;大文件下载不支持续传,需换镜像重试 |
### 鸿蒙 CMP 填坑清单
- **单一 KN 运行时**:libkn.so 内含全部逻辑层(export shared);同进程禁止双 runtime——双 so 并存 = SEGV/死锁
- **skikobridge 链接**:宿主 CMake 必须链接 `compose::skikobridge`,否则 CMP 组件首次挂载即 SEGV
- **生命周期转发**:宿主页必须转发 onPageShow/Hide、onSurfaceShow/Hide 给 controller,否则启动/锁屏异常
- **release 链接**:debug 产物存在 skia FontCollection 部分链接崩溃(IrLinkageError),鸿蒙包一律用 release 产物
- **状态保留**:surface 重建 → 组合树重建是平台确定性行为,靠 ViewModel 单例 + Intent 幂等兜底;Navigation 承载方案已验证并回退
## 📖 文档索引
| 文档 | 内容 |
|---|---|
| [docs/HUAWEI-KMP-CMP-SOLUTION.md](docs/HUAWEI-KMP-CMP-SOLUTION.md) | 华为 KMP&CMP 方案详解(工具链/架构/接线六步/坑清单) |
| [docs/HUAWEI-CMP-MIGRATION.md](docs/HUAWEI-CMP-MIGRATION.md) | CMP 迁移设计与进度 |
| [docs/CMP-HOME-MVI-DESIGN.md](docs/CMP-HOME-MVI-DESIGN.md) | 首页 MVI 架构设计 |
| [docs/CMP-ASSESSMENT.md](docs/CMP-ASSESSMENT.md) | CMP 选型评估(结论反转记录) |
| [docs/HARMONY-CRASH-POSTMORTEM.md](docs/HARMONY-CRASH-POSTMORTEM.md) | 鸿蒙崩溃复盘(skikobridge 缺链定位全过程) |
| [docs/IOS-CRASH-POSTMORTEM.md](docs/IOS-CRASH-POSTMORTEM.md) | iOS 首启 5 层问题全链复盘 |
| [docs/KMP-NETWORK-DESIGN.md](docs/KMP-NETWORK-DESIGN.md) | 网络层设计 |
| [docs/LOGIN-MODULE-DESIGN.md](docs/LOGIN-MODULE-DESIGN.md) | 登录域独立模块设计 |
| [docs/TARGET-ACTION-MIGRATION.md](docs/TARGET-ACTION-MIGRATION.md) | 路由协议迁移 |
| [docs/ARCHITECTURE.md](docs/ARCHITECTURE.md) | 逻辑层架构学习指南 |
| [harmonyApp/README.md](harmonyApp/README.md) | 鸿蒙工程构建细节 |
## ⭐ 支持一下
如果这个仓库帮你少踩了坑,欢迎点个 Star —— 鸿蒙 × CMP 的公开实践还太少,你的 star 能让更多正在填坑的人搜到它。
---
**Kotlin 跨端 · 鸿蒙生态 · 持续填坑中**