适配信息
- 适配仓库(oh-tpc 官方迁移仓):https://atomgit.com/oh-tpc/Circuit
- 鸿蒙化代码分支:
ohos/Circuit(PR 已提交,合并后生效)- 上游库:slackhq/Circuit
- 适配路线:纯 ArkTS 语义复刻 + 桥接 @kit;代码提交于
ohos/目录(符合官方「在对应分支创建鸿蒙化目录」要求,不编译上游 Kotlin 源码)- 适配所用上游版本:0.39.0(GitHub/Maven Central 最新 release,已核对)
- 运行环境:HarmonyOS SDK 6.0.0(20) / Kotlin 2.2.21 / KMP 2.2.21 / CMP 1.9.2 / DevEco Studio 26.0.0(DevEco 26 版本)
- 环境搭建指引:https://atomgit.com/wdracky/kmp-ohos-adapters/blob/main/环境搭建指引.md
三方库适配征文 · 第二批 · 上游 slackhq/Circuit 0.39.0
摘要
本文记录 Slack 开源架构库 Circuit 0.39.0 在 OpenHarmony 上的适配实践。Circuit 是纯 KMP+Compose 架构库,不含平台 API,全部由「契约 + 内存结构」构成,因此适配重点不在 API 桥接,而在语义逐条对齐。文章采用「纯 ArkTS 语义复刻 + 桥接 @kit」路线,将实现拆为语义层(Screen / Navigator / Presenter / PopResult / RetainedStateRegistry)与引擎层(OhosRouterNavigator / OhosRetainedStore),逐条守住返回栈的三条不变式(根 Screen 不可弹出、pop 必须回传来源 Screen、resetRoot 后深度恒为 1),并用页面生命周期模拟 RetainedState 的两段式恢复。全文给出实测环境版本、适配映射表、核心代码、踩坑记录与 FAQ,可在 DevEco Studio 26 + HarmonyOS SDK 6.0.0(20) 下直接复现。
目录
一、Circuit 是什么
Circuit 是 Slack 的 KMP+Compose 架构库:以 Screen 为最小 UI 单元,Presenter 生产状态、Navigator 管导航、RetainedState 跨配置变更保持状态,Screen 间可回传结果。它把「屏幕 + 状态 + 导航 + 状态保持」收敛为一套跨平台契约。
环境搭建
在动手复刻之前,先把工具链对齐。本文全部代码在以下环境实测通过:

| 组件 | 版本 | 说明 |
|---|---|---|
| DevEco Studio | 26.0.0(DevEco 26 版本) | 鸿蒙官方 IDE,负责工程创建、编译与真机调试 |
| HarmonyOS SDK | 6.0.0(20) | 对应 API 20,ArkTS 严格模式 |
| Kotlin | 2.2.21 | 上游 Circuit 0.39.0 的编译版本 |
| KMP(Kotlin Multiplatform) | 2.2.21 | 与 Kotlin 版本保持一致 |
| CMP(Compose Multiplatform) | 1.9.2 | 上游 UI 层依赖版本 |
搭建步骤:
- 安装 DevEco Studio 26(DevEco 26 版本),首次启动时按向导下载 HarmonyOS SDK 6.0.0(20)。
- 新建一个 Empty Ability 工程,确认
build-profile.json5中compileSdkVersion为6.0.0(20)。 - 若需对照上游 Kotlin 侧实现,安装 Kotlin 2.2.21 与 KMP 2.2.21 插件,CMP 依赖锁定 1.9.2。
- 将
ohos/src/下的.ets文件复制进工程ets/目录,在页面中import即可运行验收页。
完整的环境搭建指引(含 SDK 下载、环境变量、常见报错处理)见:
https://atomgit.com/wdracky/kmp-ohos-adapters/blob/main/环境搭建指引.md
二、适配路线
| Circuit(Kotlin) | 鸿蒙语义层 | 鸿蒙引擎层 |
|---|---|---|
Screen(key + Ui) | Screen interface | — |
Navigator | Navigator interface + CircuitBackStack | OhosRouterNavigator(pushUrl/back/clearAndPushUrl) |
Presenter<S>.present() | Presenter<S> interface | — |
PopResult | PopResult interface | — |
rememberRetained | RetainedStateRegistry | OhosRetainedStore(onPageHide/Show 驱动) |
| Screen → 路由 | key→路由名映射 | routeFor()(detail/xxx→DetailDemo) |
整体分层与调用关系如下:
三、核心实现:返回栈的三条不变式
Circuit 的 Navigator 有三条必须守住的不变式,语义层逐条实现:
public pop(result?: PopResult): boolean {
if (this.screens.length <= 1) {
return false; // 不变式 1:根 Screen 不可弹出
}
const popped: Screen = this.screens.pop() as Screen;
this.lastResult = result ?? { fromScreenKey: popped.key, payload: {} };
return true; // 不变式 2:pop 必须回传来源 Screen
}
public resetRoot(screen: Screen): void {
this.screens = [screen]; // 不变式 3:resetRoot 后 depth 恒为 1
this.lastResult = null;
}
引擎层 OhosRouterNavigator 把这三条不变式同步到 ArkUI Router:goTo→pushUrl、pop→getRouter().back()、resetRoot→clearAndPushUrl。语义栈与路由栈始终一致,验收页可对 snapshotKeys() 直接断言。
三条不变式与引擎层同步的完整流程:
四、RetainedState:两段式恢复
Circuit 的 RetainedState 解决「配置变更后状态不丢」。鸿蒙引擎层用页面生命周期模拟:
// OhosRetainedStore
onHostHide(): void { this.registry.saveAll(); } // 对应 ON_STOP
onHostShow(): void { this.registry.restoreAll(); } // 对应 ON_START
语义层 saveAll() 保留已求值项、restoreAll() 清空 provider——重建后 getValue(key) 直接命中缓存,provider 不再执行。验收页「模拟隐藏→恢复」按钮验证保持值不丢。
两段式恢复的时序如下:
五、踩坑记录
- 两层导航的边界:ArkUI Navigation 要求两页同容器,与「两页各自独立」验证目标冲突;引擎层选择 Router(各页独立),Screen→路由映射放在引擎层,语义层零平台知识。
- getRouter() 的获取时机:需在页面 onReady/aboutToAppear 之后
getUIContext().getRouter(),过早获取为空。 - 结果载荷类型:ArkTS 禁
unknown,PopResult 的 payload 用Record<string, Object>显式约束,跨层传递零丢失。
六、总结
Circuit 是纯架构库——没有一行平台 API,全部是「契约 + 内存结构」。这类库的适配重点是不变式逐条对齐(根不可弹/resetRoot 深度/pop 回传),以及把生命周期事件正确接到引擎层。本系列已完成 MVIKotlin、Kodein-DI、KMM-ViewModel 等架构/状态库,Circuit 补上了「Screen 级导航 + 状态保持」这块拼图。
一句话总结:Circuit 这类「零平台 API」的架构库,鸿蒙化的关键不是翻译代码,而是把上游的不变式与生命周期语义一条条对齐到 ArkUI 的 Router 与页面生命周期上;语义层保持零平台知识,平台差异全部收敛在引擎层,是这类库可复用、可验收的适配范式。
常见问题(FAQ)
Q1:鸿蒙化代码在哪里?为什么官方仓默认分支看不到?
A:鸿蒙化代码提交在 ohos/Circuit 分支的 ohos/ 目录下(语义层 + 引擎层 + 示例 + 博客)。PR 合并前,官方仓默认分支仍是上游镜像,因此看不到 ohos/ 目录;合并后即可在默认分支查看。
Q2:为什么是 ArkTS 语义复刻,而不是直接编译上游 Kotlin/Native 源码?
A:OpenHarmony 的 ArkTS 运行时不直接执行 Kotlin/Native 产物。本路线用 ArkTS 等价复刻上游核心语义(接口 / 算法 / 状态机),并通过桥接 @ohos.* 系统能力实现真实能力,可在 DevEco 中独立编译验证,不依赖上游构建链。
Q3:如何本地验证?
A:将 ohos/src/ 下的 .ets 文件复制到你的 HarmonyOS 工程 ets/ 目录,在页面中 import 即可;各库均提供 Demo 验收页(见本系列 demo 工程),多数能力无需密钥或联网即可跑通。
Q4:上游版本如何对应?
A:各库 ohos/README.OpenHarmony.md 声明了所适配的上游版本与 tag/commit,请以该文件为准。
Q5:DevEco Studio 用哪个版本?低版本能跑吗?
A:本文实测环境为 DevEco Studio 26(DevEco 26 版本) + HarmonyOS SDK 6.0.0(20)。低版本 IDE 可能缺少 API 20 的 SDK 与 ArkTS 严格模式校验,建议直接对齐 26 版本,避免因工具链差异导致的编译报错。
Q6:Kotlin / KMP / CMP 版本必须严格一致吗?
A:建议对齐 Kotlin 2.2.21、KMP 2.2.21、CMP 1.9.2,这是上游 Circuit 0.39.0 的编译基线。版本错配时,Kotlin 侧对照代码可能出现 API 差异,但鸿蒙侧 ArkTS 复刻代码本身不依赖这些版本,可独立编译运行。
Q7:环境搭建遇到问题怎么办?
A:优先查阅环境搭建指引:https://atomgit.com/wdracky/kmp-ohos-adapters/blob/main/环境搭建指引.md ,其中覆盖了 SDK 下载失败、环境变量未生效、真机签名等常见问题;仍无法解决可在 KMP&CMP 鸿蒙社区提问。
欢迎加入 KMP&CMP 鸿蒙社区:https://atomgit.com/CPF-KMP-CMP
本文详述Slack开源KMP架构库Circuit 0.39.0在OpenHarmony平台的适配实践,采用纯ArkTS语义复刻策略,不编译Kotlin源码,聚焦返回栈三条不变式(根不可弹出、pop回传来源、resetRoot深度归1)与RetainedState两段式恢复机制。通过语义层与引擎层分离设计,将ArkUI Router和页面生命周期精准映射,实现在DevEco Studio 26 + HarmonyOS SDK 6.0.0(20)环境下的可验证复现。

3230

被折叠的 条评论
为什么被折叠?



