Skip to content

晞晗IM(XHIM)HarmonyOS / OpenHarmony 接入指南

当前状态:arm64-v8a 闭源动态内核、Node-API Bridge、ArkTS Promise Facade、 会话/聊天 ArkUI 组件、可选通话控件、类型声明和 QuickStart 已提交,Bridge 已使用 OHOS aarch64 工具链通过 -Werror 语法门禁。正式售卖前仍需 DevEco HAR 构建、签名、真机和企业 OHPM 消费验证。

第一次在空白工程接入? 请先看 HarmonyOS 从零快速接入。本页保留 Node-API、Native toolchain、HAR 发布和高级二次开发细节。 listDeviceSessionsrevokeDeviceSession、requestId 取消及同步策略示例见 HarmonyOS QuickStart 的登录设备管理

1. 商用交付边界

推荐把稳定 C ABI 内核和 Node-API Bridge 作为两个闭源 .so 随 HAR 分发。 Bridge 只链接 C ABI,两个库不跨边界传递 C++ 对象:

text
xhim.har
└── package/
    ├── oh-package.json5
    ├── Index.ets / index.d.ets
    ├── libs/arm64-v8a/libxhim_napi.so
    ├── libs/arm64-v8a/libxhim_core_v1.so
    ├── ets/                       ArkTS Headless Facade
    ├── resources/
    ├── README.md
    ├── LICENSE
    ├── NOTICE
    └── CHANGELOG.md

是否支持 x86_64 模拟器或其他设备 ABI 必须写入版本矩阵。不要只用 arm64 archive 编译成功就宣称 HAR 已可用;最终 HAR 必须同时消费完整链接的 libxhim_core_v1.solibxhim_napi.so

HarmonyOS/OpenHarmony 使用 Node-API 连接 ArkTS 与 C/C++。参考: OpenHarmony NDK 概览。 HAR/OHPM 的引用和发布规则参考: 引用共享包ohpm publish

2. 客户项目接入(目标发行包)

正式发布后,企业 OHPM:

json5
{
  "dependencies": {
    "@<scope>/xhim": "<version>"
  }
}

本地验收可以临时使用:

json5
{
  "dependencies": {
    "@<scope>/xhim": "file:../artifacts/xhim.har"
  }
}

客户只 import ArkTS Facade,不直接 import 内部测试 .so、调用 Node-API C 函数或链接 C++ 静态库。

3. 当前 arm64 源码验证

使用 DevEco/OpenHarmony Native toolchain:

bash
cmake -S /path/to/xhim -B /path/to/build-xhim-ohos-arm64 \
  -DCMAKE_TOOLCHAIN_FILE="$OHOS_NDK/build/cmake/ohos.toolchain.cmake" \
  -DOHOS_ARCH=arm64-v8a \
  -DCMAKE_BUILD_TYPE=Release \
  -DXHIM_BUILD_SHARED=ON \
  -DXHIM_BUILD_TESTS=OFF \
  -DXHIM_BUILD_PACKAGE_TESTS=OFF \
  -DXHIM_BUILD_EXAMPLES=OFF \
  -DXHIM_WARNINGS_AS_ERRORS=ON
cmake --build /path/to/build-xhim-ohos-arm64 --parallel

正式 libxhim_core_v1.so 必须使用经过审计的 SQLCipher Target 和生产 Product Adapter 完整链接;只注入编译接口的 SQLite::SQLite3 仅可用于语法 验证,不得打进 HAR。libxhim_napi.so 只承担 Node-API 注册与参数复制,不复制 数据库、同步或网络状态机。

多个 .so/HAR 同时使用 C++ runtime 时必须采用一致的 DevEco/Clang 主版本与 libc++ 策略,避免跨 runtime 交换 C++ 对象。XHIM 对 ArkTS 只暴露 C/Node-API 边界。参考: HarmonyOS libc++ 说明

4. ArkTS Facade API

typescript
// 实际入口:src/main/ets/xhim/XHIMClient.ets
const policy: XHIMRuntimePolicy = {
  requestTimeoutMilliseconds: 15000,
  syncPageSize: 200,
  reconnectInitialDelayMilliseconds: 1000,
  reconnectMaxDelayMilliseconds: 30000,
  maxReconnectAttempts: 8
}

export class XHIMClient {
  static connect(
    context: common.Context,
    server: string,
    userId: string,
    options?: XHIMConnectOptions
  ): Promise<XHIMClient>
  start(): Promise<void>
  login(accountHint: string, accessToken: string): Promise<void>
  updateCredential(accessToken: string): Promise<void>
  logout(): Promise<void>
  sendText(
    conversationId: string,
    text: string,
    clientMessageId?: string
  ): Promise<ArrayBuffer>
  sendMessage(
    conversationId: string,
    message: XHIMOutgoingMessage,
    clientMessageId?: string
  ): Promise<ArrayBuffer>
  retryMessage(clientMessageId: string): Promise<ArrayBuffer>
  cancelMessage(clientMessageId: string): Promise<ArrayBuffer>
  message(clientMessageId: string): Promise<XHIMMessage>
  messages(
    conversationId: string,
    cursor?: ArrayBuffer,
    limit: number = 50
  ): Promise<XHIMMessagePage>
  conversations(
    cursor?: ArrayBuffer,
    limit: number = 50
  ): Promise<XHIMConversationPage>
  markConversationRead(
    conversationId: string,
    throughServerSequence: number
  ): Promise<XHIMConversationReadReceipt>
  state(): XHIMClientState
  onEvent(listener: XHIMEventListener): () => void
  shutdown(): Promise<void>
  destroy(): void
}

投影事件提供 MESSAGE_UPSERTED/MESSAGE_STATE_CHANGED/ CONVERSATION_CHANGED/SOCIAL_CHANGED/SYNC_APPLIED,统一 projectionChange 含 origin、scope、IDs、revision、sequence 和账号 fence。 XHIMProjectionRequeryController 会先注册 listener 再首次查询,并在 ArkTS 事件循环串行、合并触发公开 SDK 查询。未知 kind/schema 映射为 PROJECTION_INVALIDATED 并宽范围重查;N-API 在 native 回调返回前复制全部 字段,ArkUI 不直接读取 SQLite。

推荐应用使用 XHIMClient.connect(...)。它只读取公开 GET /v1/sdk/config,自动建立账号隔离存储、启动和登录;Development 模式 按需调用 POST /v1/sdk/development:login,Production 使用 XHIMAuthentication.business(provider) 或受控的固定 Token。Provider 在 CREDENTIAL_REQUIRED 时自动再次调用,业务页面不管理 Token 生命周期。

Discovery 使用系统 @kit.NetworkKit,关闭缓存和重定向,单次响应上限 256 KiB。Server URL 拒绝 userinfo/query/fragment;HTTP 仅允许公开配置为 Development 的服务端。客户端接口从不接受 Admin Key。完整空白工程代码见 QUICKSTART.md;一键 Discovery 的 maxRedirects=0 需要 HarmonyOS API 23。

运行策略和 XHIMDeployment 在创建 Client 时复制并固定。客户可提供 Bootstrap URL、Endpoint Key ID 和 Ed25519 公钥;实际 Endpoint 必须验签且未过期。 ArkTS 业务侧不能关闭 TLS/签名校验。

Index.ets 同时导出 XHIMConversationListXHIMChatViewXHIMCallControls;Node-API 类型声明位于 src/main/cpp/types/libxhim_napi/index.d.ts

Cursor 和 Payload 使用 ArrayBuffer/Uint8Array,不能按 UTF-8 字符串解释。 Facade 对业务暴露 ArkTS model,不暴露 napi_env、native pointer 或 C struct。

4.1 已读调用

messages 返回最新消息在前的页面;nextCursor 是不透明二进制值,只能原样 用于同账号、同会话的下一页。聊天页已经展示一批服务端确认消息后,提交其中 最大的正 serverSequence

typescript
const page = await client.messages(conversationId)
const serverSequences = page.messages
  .filter((message: XHIMMessage) => message.serverSequence !== undefined)
  .map((message: XHIMMessage) => message.serverSequence as number)
if (serverSequences.length === 0) {
  return
}
const highestVisibleServerSequence = serverSequences.reduce(
  (highest: number, sequence: number) => Math.max(highest, sequence),
  0
)

const receipt = await client.markConversationRead(
  conversationId,
  highestVisibleServerSequence
)
// 使用 receipt.unreadCount 更新 UI。

Pending/Sending 消息必须排除,Promise 完成前不要乐观清零。监听 { type: 'conversationReadChanged', conversationId } 后重新加载该会话摘要; 同值或更低值为幂等 no-op。ArkTS number 只能精确表示到 Number.MAX_SAFE_INTEGER,Facade 会拒绝更大的序号,不能容忍精度截断;未来 若产品服务端可能越过该范围,公共模型必须迁移到经过支持矩阵验证的 bigint 或不透明整数类型。

conversations 返回置顶优先、随后按持久活动排序的摘要页。业务不得通过 Node-API 私有方法、SQLite、数组下标或设备时间构造服务端序号。

实时 Presence/Typing(非持久投影)

publishPresence/publishTyping 使用当前已登录会话,ArkTS 页面不传 Token。 权威回显与 PRESENCE_CHANGED/TYPING_CHANGED 事件都包含服务端 sequenceexpiresAtMilliseconds。它们不落 SQLite、也不是投影失效事件;UI 到期 必须清除,输入状态仅发布开始/停止转换,禁止每次按键调用。

4.2 自定义消息与 ArkUI Renderer

业务插件注册精确 contentType,发送前先校验版本化信封:

typescript
const plugins = new XHIMMessagePluginRegistry()
plugins.register(productCardPlugin)
const outgoing: XHIMOutgoingMessage = {
  contentType: 'com.xihan.product-card',
  contentVersion: 1,
  payload: cardPayload,
  fallbackText: '[商品卡片]'
}
plugins.validate(outgoing)
await client.sendMessage(conversationId, outgoing)

读取消息后调用 plugins.presentation(message)。插件未注册、不支持新版本、 Payload 校验失败或抛错时,Registry 使用 fallbackText;Core 仍原样保存 ArrayBuffer,不会阻止时间线 Cursor 推进。

Registry 属于创建它的 ArkTS isolate,不跨 Worker/VM 共享。多 Worker 产品应 在各自 isolate 注册相同的纯业务插件,通过 XHIM 事件/IPC 传递复制后的信封, 不能共享可变 Map 或 Renderer 对象。

ArkUI 业务组件可通过 XHIMMessageRendererRegistry 注册 XHIMMessageItemRenderer。Renderer 只处理复制后的 ArkTS 模型,异常时回退基础 MessageItem;不得直接 import Node-API .so、读取 SQLite 或持有 native 对象。完整示例见 examples/QuickStart.ets

5. Node-API Bridge 规则

  • 一个 ArkTS Client 对象通过 napi_wrap 绑定一个 native Wrapper;finalizer 只做泄漏兜底,业务仍显式 shutdown/close
  • napi_env 和普通 napi_value 不能从创建线程直接跨线程使用;
  • XHIM Callback 来自私有 native 线程,Bridge 必须使用平台支持的线程安全 异步投递机制回到 ArkTS runtime;
  • Callback 内先深拷贝 byte view、消息数组和 Cursor,再离开 C ABI;
  • 每个 Promise 对应一个 request context,同步拒绝或最终 Completion 恰好释放 一次;
  • Native Promise 提交后立即带 requestId;Facade 可通过 XHIMRequestObserver 获取并调用 cancelRequest(),取消不回滚已提交事务;
  • XHIMNativeError 完整复制 domain/stableCode/nativeCode/retryable/retryAfterMilliseconds/userAction/operationId/traceId,业务不解析 message;
  • CREDENTIAL_REQUIRED 后只调用 updateCredential,继续沿用 Core 中的 canonical account;
  • cancelMessage 只取消尚未被 Transport 获取的本地 Outbox,不等于服务端撤回;
  • 已读 Promise 必须返回 native receipt;ArkTS 不维护第二份 read sequence, 也不在服务端确认前清零;
  • 环境清理钩子先停止新请求、取消订阅、shutdown,再释放 native handle;
  • Node-API 异常/错误对象不能越过 C Callback;映射为稳定 ArkTS Error;
  • 不在 Node-API callback 中同步等待另一个 XHIM callback。

OpenHarmony 的 napi_wrap/napi_unwrap 用法可参考: Node-API class/object binding

6. 生命周期和 Ability

Client 由账号级 Service/Repository 拥有,不由 Page 或单个 UIAbility 页面对象 拥有。页面销毁与前后台切换不能重复打开同一数据库。

text
应用进程/账号容器启动 → create/start
账号登录             → login,等待 Ready
页面切换/重建         → 复用 Client
账号退出             → logout(清除自动续凭 Provider)
Ability/进程受控结束  → shutdown/close

多个 Ability 或 Extension 需要共享 IM 时,应建立单一 Owner/IPC 方案;当前不 支持多个进程同时打开同一个 storage profile。

7. 存储和安全

  • 使用应用沙箱持久文件目录的账号隔离子目录,不放 cache、公共下载或安装资源;
  • Token 和密钥接入 HUKS/平台安全存储,不进入 Preferences 明文;使用 HUKS Key 包装随机 SQLCipher 数据库密钥;
  • oh-package.json5、日志和诊断包不得包含服务地址密钥、Token 或签名 URL;
  • 数据库、WAL/SHM、媒体临时文件执行一致的备份、清除和升级策略;
  • profile 目录名使用不透明 ID;
  • 关闭 Client 后再删除账号数据。

8. 附件消息与可选实时音视频

  • XHIMAttachmentPicker 基于系统 CameraPicker、PhotoViewPicker 和 DocumentViewPicker,目标 API 11+;
  • Picker/FileShare URI 的授权可能有生命周期;上传完成前安全持有,不把临时 URI 持久化为消息 Payload;
  • XHIMMediaMessaging 默认创建 Core 持久化上传任务;Hash、鉴权、断点续传、 校验、崩溃恢复和依赖消息入队由 Core 负责;
  • API 返回只表示 durable admission;事件只携带 task hint,完整状态通过 mediaTask() 查询;cancelMediaTask() 与通用 cancelRequest() 不同;
  • acceptMediaDownload() 通过稳定 XHIMMediaRef 和扁平 cacheKey 创建 私有缓存下载;不得从 storagePath 推导缓存路径;
  • 完成后只通过 XHIMMediaCacheReader.read/stream 消费已校验字节;Node-API worker 承担 open/SHA-256/read,Promise chain 串行单 Reader;
  • Product Adapter 没有安全 reader 时明确返回 media_cache_unsupported(103),不退化为 ArkTS 路径访问;
  • task、event、diagnostics() 和网络 Payload 都不包含路径、Token、临时 URL 或签名 URL;diagnostics 是同步、只读、纯内存快照;
  • 系统网络恢复回调只需调用 notifyNetworkAvailable();它是无 request ID 的同步 best-effort 提示,Core 重连定时器继续保底;
  • XHIMMediaUploader 仅作为旧版兼容迁移 SPI,不是默认商用接入路径;
  • 实时音视频后续以独立 @<scope>/xhim-call 接入厂商 Provider,不阻塞纯 IM 首发;
  • 麦克风/摄像头权限、音频焦点/路由、后台来电和厂商 RTC 对象不得穿过稳定 xhim_v1 C ABI,也不得放入消息 Payload。

9. UI Kit 与二次开发

建议:

text
@<scope>/xhim             Headless ArkTS Facade + native
@<scope>/xhim-ui          ArkUI 会话/聊天/联系人组件
@<scope>/xhim-call        RTC 与系统桥

UI Kit 只依赖 Headless Facade。主题、资源、头像加载、路由、菜单和自定义消息 Renderer 都通过 ArkTS 接口注入,组件不得直接 import libxhim_napi.so 或读库。

ArkUI 组件使用 resources/base/media/xhim_ic_*.svg 中的 Lucide 图标和 XHIMColors 默认设计令牌。HAR 必须保留 ThirdPartyLicenses;来源、固定 Commit 和 SHA-256 见 五端 UI 资源说明

10. 发布验收

  • 空白 ArkTS 工程只依赖最终 HAR/OHPM 包即可构建;
  • arm64 真机及声明支持的模拟器/设备 ABI 全部验证;
  • Node-API Promise、订阅、环境清理、前后台和进程重启无悬空 context;
  • Product Adapter、SQLite、libc++ 和所有 .so 的架构/版本一致;
  • HAR 发布内容包含非空 README、LICENSE、CHANGELOG、NOTICE、版本清单、Hash 和 SBOM;
  • debug HAR 不被误发布为闭源商用品;
  • 删除 UI 包后 Headless SDK 仍可完整使用。

11. OfflineReader 平台契约

XHIMOfflineReader 覆盖消息、搜索、会话和六类社交分页,并复用 XHIMClient 的平台模型映射。

  • N-API 使用 napi_async_work 执行同步 C ABI/SQLite;ArkTS facade 用恢复型 Promise 链严格串行同一句柄;
  • N-API 在 worker 返回前深拷贝 page、嵌套字段和 opaque cursor;
  • close() 异步、幂等,先禁止新请求,再排在已接收请求之后销毁;
  • 数据库 Key 只通过 XHIMOfflineDatabaseKeyProvider 从 HUKS 保护的安全层 注入;ArkTS 临时 ArrayBuffer 和 native 临时 vector 都会清零;
  • native Error 保留未知原始 code;平台模型保留 nativeStatenativeRolenativeContentKindnativeMutationKind

接入示例见 HarmonyOS 快速接入。禁止把 正式 Key 写入 ArkTS、resources、Preferences、日志或 HAR。

XHIM 客户端 SDK 与服务端文档