熊猫钓鱼>_>头像
Kotlin Multiplatform for OpenHarmony 实战:Circuit 架构库的 ArkTS 语义复刻封面图

Kotlin Multiplatform for OpenHarmony 实战:Circuit 架构库的 ArkTS 语义复刻

适配信息

  • 适配仓库(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 Studio26.0.0(DevEco 26 版本)鸿蒙官方 IDE,负责工程创建、编译与真机调试
HarmonyOS SDK6.0.0(20)对应 API 20,ArkTS 严格模式
Kotlin2.2.21上游 Circuit 0.39.0 的编译版本
KMP(Kotlin Multiplatform)2.2.21与 Kotlin 版本保持一致
CMP(Compose Multiplatform)1.9.2上游 UI 层依赖版本

搭建步骤:

  1. 安装 DevEco Studio 26(DevEco 26 版本),首次启动时按向导下载 HarmonyOS SDK 6.0.0(20)。
  2. 新建一个 Empty Ability 工程,确认 build-profile.json5 中 compileSdkVersion 为 6.0.0(20)。
  3. 若需对照上游 Kotlin 侧实现,安装 Kotlin 2.2.21 与 KMP 2.2.21 插件,CMP 依赖锁定 1.9.2。
  4. 将 ohos/src/ 下的 .ets 文件复制进工程 ets/ 目录,在页面中 import 即可运行验收页。

完整的环境搭建指引(含 SDK 下载、环境变量、常见报错处理)见:
https://atomgit.com/wdracky/kmp-ohos-adapters/blob/main/环境搭建指引.md

二、适配路线

Circuit(Kotlin)鸿蒙语义层鸿蒙引擎层
Screen(key + Ui)Screen interface—
NavigatorNavigator interface + CircuitBackStackOhosRouterNavigator(pushUrl/back/clearAndPushUrl)
Presenter<S>.present()Presenter<S> interface—
PopResultPopResult interface—
rememberRetainedRetainedStateRegistryOhosRetainedStore(onPageHide/Show 驱动)
Screen → 路由key→路由名映射routeFor()(detail/xxx→DetailDemo)

整体分层与调用关系如下:

Circuit 上游 Kotlin 契约

鸿蒙语义层(纯 ArkTS)

鸿蒙引擎层(桥接 @kit / @ohos)

ArkUI Router / 页面生命周期

CircuitBackStack 返回栈

RetainedStateRegistry 状态注册表

OhosRouterNavigator

OhosRetainedStore

三、核心实现:返回栈的三条不变式

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() 直接断言。

三条不变式与引擎层同步的完整流程:

goTo

pop

是

否

resetRoot

Navigator 操作

操作类型

push 新 Screen 入栈

栈深度 <= 1 ?

返回 false:根 Screen 不可弹出

弹出栈顶并回传 PopResult

栈重置为单元素,depth 恒为 1

OhosRouterNavigator 同步路由栈

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 不再执行。验收页「模拟隐藏→恢复」按钮验证保持值不丢。

两段式恢复的时序如下:

"Provider 缓存" "RetainedStateRegistry" "OhosRetainedStore" "页面生命周期" "用户/系统" "Provider 缓存" "RetainedStateRegistry" "OhosRetainedStore" "页面生命周期" "用户/系统" "页面隐藏(ON_STOP)" "onHostHide()" "saveAll()" "保留已求值项" "页面显示(ON_START)" "onHostShow()" "restoreAll()" "清空 provider,仅留缓存" "getValue(key) 直接命中,provider 不再执行"

五、踩坑记录

  1. 两层导航的边界:ArkUI Navigation 要求两页同容器,与「两页各自独立」验证目标冲突;引擎层选择 Router(各页独立),Screen→路由映射放在引擎层,语义层零平台知识。
  2. getRouter() 的获取时机:需在页面 onReady/aboutToAppear 之后 getUIContext().getRouter(),过早获取为空。
  3. 结果载荷类型: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

转载自 CSDN-专业IT技术社区

原文链接:https://blog.csdn.net/ailuloo/article/details/168346547

文章来源转载

评论

评论列表

微信小程序
QQ小程序

关于作者

点赞数:0
关注数:0
粉丝:0
文章:0
关注标签:0
加入于:--