主题
晞晗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 发布和高级二次开发细节。
listDeviceSessions、revokeDeviceSession、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.so 和 libxhim_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 同时导出 XHIMConversationList、XHIMChatView 和 XHIMCallControls;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 事件都包含服务端 sequence 和 expiresAtMilliseconds。它们不落 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_v1C 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;平台模型保留nativeState、nativeRole、nativeContentKind和nativeMutationKind。
接入示例见 HarmonyOS 快速接入。禁止把 正式 Key 写入 ArkTS、resources、Preferences、日志或 HAR。