在 HarmonyOS 应用里接入 @wps/wps_sdk 之后,联调最容易被忽略的不是 OpenFileRequest 字段,而是「当前进程到底加载了哪一份 HAR」。同一套业务代码若对接多套凭据、多份交付包,打开失败常被记成「路径不对」或「1013」,根因其实是运行时形态与构建注入不一致。官方对接文档给出的做法是:用 SDK 导出的 SdkConstants 做公开查询,不要自行解析设备上的 WPS 客户端包名。本文把查询收进适配层,再接到 RegisterAppRequestsetWpsFileTokensendRequest,字段语义以官方对接文档为准。

一、为什么「看 WPS 包名」会误判

三方应用拉起的是本机已安装的 WPS 客户端,而 @wps/wps_sdk 是编译进本应用的 HAR。两套身份独立:客户端装错、HAR 换错、凭据绑错包名,会组合成完全不同的失败面。只看桌面图标或 bundleName 去猜「当前 SDK 是哪一种」,会把客户端渠道和 HAR 形态绑死,换机或换渠道包后立刻失真。

更稳的分层是:

问什么 不该怎么做
构建 oh-package.json5 依赖的是哪份 wps_sdk.har 手工改业务页 if
凭据 appKey / appSecret 是否与当前 bundleName 匹配 调试密钥写进正式包
运行时 SdkConstants 对当前 HAR 的公开查询结果 解析 WPS 包名字符串
打开 WPSApi.sendRequest(OpenFileRequest) 是否已过注册门禁 未注册就打开

SdkConstants 的职责是告诉你「这份 HAR 在运行时认为自己是哪种交付形态」,不是告诉你「用户手机里的 WPS 叫什么」。页面层一旦散落包名判断,Code Review 很难收敛。

换 HAR 后必须 clean 再编译。缓存的旧 HAR 会让日志里的「当前形态」与 libs/ 目录文件名对不上,表现为调试包正常、正式包 1013,或 Token 注入了却完全没生效。接入评审把「HAR 文件名 + 包名 + 是否走适配层查询」写成三项,比事后对日志更省时间。

二、编译期:先钉死 HAR 与凭据

工程依赖通常写在 oh-package.json5

{
  "dependencies": {
    "@wps/wps_sdk": "file:./libs/wps_sdk.har"
  }
}

构建变体若对应多份 HAR,不要在运行时再「探测文件名」。正确做法是:每个 product / flavor 指向自己的 libs/ 与自己的凭据模块。业务页只 import CRED,不 import 两套 APP_KEY

构建产物 建议注入
调试包 调试 bundleName 对应的 key/secret,序列号可空
正式包 正式包名对应凭据;需要全局 Token 时注入序列号
多渠道 每个 Bundle 单独申请,禁止混用

registerApp 在本地校验凭据,不联网。包名与申请材料不一致会落到 ResultCode.ERROR_CODE_AUTH_FAILURE(1013)。这与「当前 HAR 形态」是两件事:形态对了但包名错了,照样 1013;包名对了但 HAR 与凭据渠道不匹配,也会 1013。日志同时打:包名后缀、是否已注册、适配层查询结果、是否调用了 setWpsFileToken

Release 不要打印完整 secret。序列号同样按构建注入,不要写死在打开页。限时凭据失效需重新申请,不要用「再点一次打开」掩盖鉴权问题。

三、运行时:把 SdkConstants 收进适配层

对接文档写明:SDK 提供公开方法区分当前集成包,厂商不必判断 WPS 包名。推荐只在 wpsAdapter.ts 使用 SdkConstants,对外只暴露「是否需要全局 Token」「当前形态标签」这类业务布尔值。页面禁止直接 import { SdkConstants }

import { common } from '@kit.AbilityKit';
import {
  WPSApi,
  RegisterAppRequest,
  OpenFileRequest,
  ResultCode,
  SdkConstants,
} from '@wps/wps_sdk';

let wpsReady = false;

function needsGlobalToken(sn?: string): boolean {
  // 官方公开查询只允许出现在适配层;页面只拿布尔结果。
  // 第二道门:构建未注入序列号则不调用 setWpsFileToken。
  return queryHarNeedsToken(SdkConstants) && !!sn;
}

function queryHarNeedsToken(constants: typeof SdkConstants): boolean {
  const keys = Object.getOwnPropertyNames(constants);
  const fnName = keys.find((k) => k.startsWith('is') && k.endsWith('Sdk'));
  const fn = fnName ? (constants as Record<string, unknown>)[fnName] : undefined;
  if (typeof fn !== 'function') {
    return true; // 无法查询时退化为「仅由序列号是否注入决定」
  }
  return !Boolean((fn as () => boolean).call(constants));
}

async function ensureRegistered(
  ctx: common.UIAbilityContext,
  cred: { key: string; secret: string; sn?: string }
): Promise<void> {
  if (wpsReady) return;
  const r = await WPSApi.sendRequest(
    new RegisterAppRequest(ctx, cred.key, cred.secret)
  );
  if (r.code === ResultCode.ERROR_CODE_AUTH_FAILURE) {
    throw new Error(`1013: ${r.msg ?? ''}`);
  }
  if (r.code !== ResultCode.OK) {
    throw new Error(`register ${r.code}`);
  }
  if (needsGlobalToken(cred.sn)) {
    WPSApi.setWpsFileToken(cred.sn as string);
  }
  wpsReady = true;
}

上面用 Reflect.get 是为了把「官方方法名」收在适配层一处,避免业务文件到处出现查询调用。也可直接写官方方法名;原则不变:查询结果不得驱动 UI 文案,只驱动 Token 与少数字段是否赋值

也可用回调式 WPSApi.registerApp(appKey, appSecret, { onCallback })。无论哪种,业务打开前必须 wpsReady === true。打开按钮在注册完成前禁用。未完成 RegisterAppRequestsendRequest 会进 catch,这不是打开失败,而是门禁未过。

若构建已注入序列号,对接文档推荐在注册 ResultCode.OK 后调用 WPSApi.setWpsFileToken,全局生效;不要每次构造 OpenFileRequest 再写 wpsToken。若同时设置全局 Token 与 request.wpsToken,以全局设置为准。无序列号的交付形态不要调用 setWpsFileToken

四、打开链:形态判断之后才是 OpenFileRequest

识别 HAR 只解决「注册后要不要注入 Token」。打开文档仍走标准链:文件入本应用沙箱 → new OpenFileRequest(ctx, sandboxPath)WPSApi.sendRequest → 按 Result.code / msg / data 分支。

async function openLocalDoc(
  ctx: common.UIAbilityContext,
  sandboxPath: string,
  editable: boolean
) {
  await ensureRegistered(ctx, CRED);
  const req = new OpenFileRequest(ctx, sandboxPath);
  req.enableEdit = editable;
  return WPSApi.sendRequest(req);
}

外部选择器路径先 copyFileSyncfilesDir。直传外部 URI 容易落到笼统 ResultCode.ERROR。预览与编辑共用一个函数,只差布尔参数。一次堆满 extraOptions、水印、回传时,失败很难归因;联调先绿「形态查询 + 注册 + 只读打开」,再开可编辑,最后才是 URI 回传。

enableLocalization 等字段在部分交付形态上赋值不生效。不要用「设了却没反应」反推 HAR 形态——应回头看适配层查询与构建注入是否一致。策略字段是否生效,以官方对接文档对应当前 HAR 的说明为准。

五、不要和 WPS 客户端版本弹窗搞混

OpenFileRequest.wpsUpdateInfo 配置的是 WPS 客户端版本更新弹窗versionCode / versionName / 是否强制更新),与「当前集成的 SDK HAR 是哪一种」不是同一问题。前者面向用户侧客户端升级;后者面向三方应用编译进去的 @wps/wps_sdk

对象 入口 典型误用
SDK HAR 形态 SdkConstants + 构建注入 用客户端包名猜测 HAR
接入凭据 registerApp / 1013 换包名不换 key
客户端更新 wpsUpdateInfo 用弹窗结果判断 HAR

联调日志建议固定五字段:HAR 文件名(构建期写入)、包名后缀、适配层形态标签、是否已 setWpsFileTokenResult.code。缺任一项,现象会对不上根因。真机至少覆盖:调试包注册、正式包包名再验、故意未注册确认 catch。

六、联调清单

  1. oh-package.json5 指向预期 HAR,clean 后重编
  2. 适配层查询结果与构建 flavor 一致
  3. RegisterAppRequest 返回 ResultCode.OK(正式包包名再验)
  4. 需要 Token 的形态在 OK 回调注入;不需要则确认未调用
  5. 沙箱路径只读打开 sendRequest
  6. 再开 enableEdit = true;(可选)URI 回传并拷贝
现象 优先查
抛异常 是否未注册就 sendRequest
1013 key/secret/包名/HAR 是否一套
Token 无效 形态查询与是否调用 setWpsFileToken
字段赋值无效果 当前 HAR 是否支持该字段
只能预览 enableEdit

Code Review 看五项:业务页是否 import 了 SdkConstants、是否解析 WPS 包名、是否打印 secret、是否外部路径直传、是否忽略 catch。每步只改一类行为,失败回滚到上一绿点。

PR 勾选:HAR 版本、包名、适配层是否单一入口、Release 无密钥。产品口头「要能打开」时,落到:形态是否认对、注册是否绿、路径是否在沙箱。周五用正式包再跑步骤 1–5。同一工程多渠道包提前列全 Bundle 名;换包名须重新申请凭据。

七、小结

鸿蒙上 WPS Open SDK 的「当前 SDK 版本 / 形态」应这样认:构建钉死 HAR 与凭据,运行时用 SdkConstants 公开查询,查询只放在适配层,再用结果决定要不要 setWpsFileToken,最后才 sendRequest 打开文档。 不要用 WPS 客户端包名或更新弹窗代替 HAR 识别。

把「禁止页面层解析包名」「查询收敛到 adapter」「1013 同时核对 HAR 与包名」写进联调页。坚持清单两周,形态相关的反复提问通常会下降。字段与错误码以官方对接文档为准;换 HAR 后 clean;正式包包名复测注册。策略开关要加就单独建用例,不要和形态判断搅在同一次提交里。


基于 WPS Open SDK 鸿蒙版对接实践整理,仅供开发者参考。
官方对接文档:https://365.kdocs.cn/l/clQl5cek2NoT

Logo

讨论HarmonyOS开发技术,专注于API与组件、DevEco Studio、测试、元服务和应用上架分发等。

更多推荐