让 VK 小程序调用 HarmonyOS 原生能力:壳子 SDK 的实现思路

让 VK 小程序调用 HarmonyOS 原生能力:壳子 SDK 的实现思路

一句话结论

目标不是让 VK 小程序认识 HarmonyOS,也不是让它直接调用项目已有的 atomicsdk

目标是提供一个 VK Runtime / 壳子 SDK :小程序仍然按 VK 的方式调用 vk.scanCode()vk.getLocation();壳子在底层把调用转发给 ArkTS 和 HarmonyOS Kit。对小程序开发者而言,底层框架是透明的。

text 复制代码
VK 小程序(HTML / CSS / JavaScript)
              │
              │ vk.scanCode()
              ▼
VK Runtime / 壳子 SDK
  - 注入 window.vk
  - 对齐 VK 的参数、返回值、错误码
  - Promise 与回调管理
  - 能力检测与权限状态
              │
              ▼
HarmonyOS WebSDK 底座
  - window.atomicsdk(内部实现,不对小程序公开)
  - ArkTS Bridge
              │
              ▼
HarmonyOS Kit
  - ScanKit、LocationKit、相册、文件等

当前的"出境服务"元服务只是验证壳子 SDK 的测试宿主,不是 SDK 的对外形态。

为什么需要壳子 SDK

如果让页面直接调用:

js 复制代码
window.atomicsdk.invokeAsyncMethod({ appMethod: 'scanCode' })

页面会依赖鸿蒙项目的私有桥协议。以后切换小程序平台、修改桥接名、统一错误码或迁移宿主,所有页面都需要改。

而壳子 SDK 对页面提供稳定的 VK 接口:

js 复制代码
const result = await vk.scanCode({
  enableAlbum: true
})

页面只依赖 VK API 契约;鸿蒙原生实现、权限逻辑和 WebView 通信细节都隐藏在壳子内部。

扫码 API 的完整调用链

vk.scanCode() 为例:

text 复制代码
1. 用户点击 VK 小程序中的"扫码"按钮
2. 页面调用 vk.scanCode(options)
3. VK Runtime 创建唯一回调名,并调用内部 atomicsdk 桥
4. ArkTS 的 AtomicAppBridge 收到 appMethod = scanCode
5. ArkTS 调用 HarmonyOS ScanKit.startScanForResult()
6. ScanKit 打开系统扫码界面
7. 用户扫码或从相册选择二维码
8. ScanKit 返回原始结果
9. ArkTS 标准化结果并回调 JavaScript
10. VK Runtime 将结果转换为 VK API 的 Promise 成功或失败结果

1. VK 小程序页面:只调用 VK API

页面可以是普通 HTML、CSS、JavaScript:

html 复制代码
<button id="scan">调用 vk.scanCode()</button>
<pre id="result"></pre>

<script>
  const resultEl = document.getElementById('result')

  document.getElementById('scan').addEventListener('click', async () => {
    try {
      const result = await vk.scanCode({ enableAlbum: true })
      resultEl.textContent = JSON.stringify(result, null, 2)
    } catch (error) {
      resultEl.textContent = JSON.stringify(error, null, 2)
    }
  })
</script>

这里的页面不需要知道 atomicsdk、ArkTS 或 ScanKit 的存在。

2. VK Runtime:把 VK API 映射到内部桥接

宿主加载小程序 WebView 前,注入下面的适配层。正式版应将它独立为可版本化的 vk-runtime.js,而不是写在页面业务代码中。

js 复制代码
(() => {
  if (window.vk) return

  let sequence = 0

  function invokeNative(appMethod, data) {
    return new Promise((resolve, reject) => {
      const bridge = window.atomicsdk
      if (!bridge || typeof bridge.invokeAsyncMethod !== 'function') {
        reject({
          code: 'VK_API_UNAVAILABLE',
          message: 'Native VK API is unavailable'
        })
        return
      }

      const callbackName = `__vk_callback_${Date.now()}_${++sequence}`
      window[callbackName] = (result) => {
        delete window[callbackName]

        if (result && result.code === 0) {
          reject(result.data || result)
          return
        }

        resolve(
          result && Object.prototype.hasOwnProperty.call(result, 'data')
            ? result.data
            : result
        )
      }

      bridge.invokeAsyncMethod({
        appMethod,
        data: data || {},
        jsMethod: callbackName
      })
    })
  }

  window.vk = Object.freeze({
    scanCode(options) {
      return invokeNative('scanCode', options)
    }
  })
})()

关键点:

  • window.vk 是唯一的公开接口。
  • window.atomicsdk 只作为壳子内部实现细节。
  • 每次调用生成独立回调名,避免并发请求互相覆盖。
  • Runtime 负责把底层回包统一包装成 Promise。

3. ArkTS Bridge:接收 Web 调用

底座通过 HarmonyOS WebView 的 JavaScriptProxy 注入 atomicsdk。ArkTS 中的桥接对象登记 scanCode

ts 复制代码
this.addMethod('scanCode', (data, callback) => {
  if (!ScanCodeUtil.isValidOptions(data)) {
    return callback.onFail(JSBridgeErrorFactory.invalidParam())
  }
  return this.scanCode(data as ScanCodeOptions, callback)
})

实际扫码实现调用 HarmonyOS 的 ScanKit

ts 复制代码
async scanCode(data: ScanCodeOptions, callback: JSBridgeCallback) {
  if (this.scanCodeInProgress) {
    return callback.onFail(
      JSBridgeErrorFactory.invalid('scanCode already in progress')
    )
  }

  this.scanCodeInProgress = true
  try {
    const options = ScanCodeUtil.normalizeScanOptions(data)
    const result = await scanBarcode.startScanForResult(getContext(this), options)
    return callback.onSuccess(ScanCodeUtil.normalizeScanResult(result))
  } catch (error) {
    return callback.onFail(ScanCodeUtil.normalizeScanError(error))
  } finally {
    this.scanCodeInProgress = false
  }
}

这层才是真正的原生能力实现:小程序没有直接访问相机,也不需要直接调用 HarmonyOS API。

4. 宿主如何加载 Demo

第一版验证可以在元服务首页增加一个"VK Demo"入口:

text 复制代码
首页 VK Demo 按钮
  → 打开本地 vk_demo.html
  → WebView 注入 window.vk
  → 页面调用 vk.scanCode()
  → 鸿蒙系统扫码

本地 HTML 的价值是将问题缩小到最小闭环:

  • WebView 是否加载成功;
  • window.vk 是否注入成功;
  • JavaScript 到 ArkTS 的异步回调是否可用;
  • 系统权限和 ScanKit 是否可用;
  • 扫码结果能否稳定回到页面。

接口扩展方式

后续每一个 VK P0 API 都遵循同一个映射模式:

VK API Runtime 映射 ArkTS/鸿蒙能力
vk.scanCode atomicsdk.scanCode ScanKit
vk.getLocation atomicsdk.getLocation LocationKit
vk.chooseImage atomicsdk.chooseImage PhotoAccessHelper / 文件选择
vk.setClipboardData atomicsdk.setClipboardData Pasteboard
vk.getNetworkType atomicsdk.getNetType NetworkKit

如果底座已有对应能力,只需完成协议适配;若不存在,则在 ArkTS 底座实现原生能力,再在 VK Runtime 暴露同名 API。

正式版必须补齐的能力

Demo 只验证"能调用",正式 SDK 还需要:

  1. 严格契约对齐:以 VK 官方文档为准,不能只模仿方法名。
  2. 统一错误码:区分用户取消、权限拒绝、设备不支持、系统异常和参数错误。
  3. 能力检测 :提供 vk.canIUse() 或等价机制。
  4. 可信来源控制 :只对登记的 VK 小程序域名或包注入 window.vk
  5. 权限治理:按接口申请最小权限,向小程序返回明确的权限状态。
  6. 版本协商:Runtime 和原生底座分别有版本号,支持灰度和兼容判断。
  7. 可观测性:记录 API 名称、耗时、结果和错误码,不记录敏感扫码内容。

总结

这套方案的本质不是"在 WebView 里塞几个 JS 方法",而是实现一个兼容 VK API 的运行时壳子:

text 复制代码
VK 小程序不变
        ↓
VK Runtime 兼容层
        ↓
统一鸿蒙原生能力底座
        ↓
HarmonyOS 系统能力

这样,VK 小程序可以在鸿蒙元服务中运行,并获得扫码、定位、相册、剪贴板等原生能力;未来接入其他小程序平台时,复用同一套鸿蒙底座,只新增对应的平台 Runtime 即可。

相关推荐
梨想橙汁1 小时前
Vue3 组合式 API 深度解析:ref/reactive 响应式,计算属性与侦听器
前端·vue.js
暖焰核心1 小时前
继承全解——继承、默认成员函数、切片、隐藏与虚继承
java·前端·javascript
by组态1 小时前
Ricon组态系统API参考手册
前端·后端·物联网
前端逗比逗1 小时前
AI 前端落地实战:SSE 流式输出、断点续传、打字机渲染
前端·webassembly
晴天162 小时前
前端跨域方案解析:JSONP 的原理、实战与演进
前端·状态模式
web打印社区2 小时前
浏览器静默打印?先别装第三个库了
前端·vue.js·chrome·electron·pdf
艾伦野鸽ggg2 小时前
前端异步请求的状态竞争(请求竞态)问题
前端·javascript·axios
是立不是利2 小时前
前端交互基石:深入剖析JavaScript三级联动背后的设计哲学
开发语言·前端·javascript
动恰客流统计2 小时前
传统红外对射客流统计为何逐步淡出主流?准确率与场景限制深度分析
大数据·前端·人工智能