H5 如何调用 ArkTS:JavaScriptProxy、requestId 和 Promise 回调

H5 如何调用 ArkTS:JavaScriptProxy、requestId 和 Promise 回调

项目地址:github.com/lichenyang5...

本文是 MiniAppRuntime-Harmony 系列的第三篇,重点讲 H5 到 ArkTS 的最小通信边界。

1. JSBridge 的本质是什么

JSBridge 听起来很抽象。

但拆开看,它其实只做两件事:

text 复制代码
H5 把请求发给 Native
Native 把结果回给 H5

问题在于,这两个动作都不是同步的。

H5 调用:

js 复制代码
window.myascf.send("ui.showToast", {
  message: "hello from h5"
})

它希望拿到的是一个 Promise:

js 复制代码
window.myascf
  .send("ui.showToast", { message: "hello from h5" })
  .then(res => console.log(res))
  .catch(err => console.error(err))

所以我们要设计一套协议,让异步请求和异步响应可以对应起来。

关键就是:requestId

2. 请求协议

H5 发给 ArkTS 的请求长这样:

json 复制代码
{
  "requestId": "req_1700000000000_1",
  "action": "ui.showToast",
  "params": {
    "message": "hello from h5"
  }
}

字段不多,但每个都很重要。

requestId 用来匹配响应。

action 表示要调用哪个能力。

params 是参数。

如果没有 requestId,H5 同时发出两个请求时,就无法知道哪个响应属于哪个请求。

3. H5 侧 send 方法

H5 侧的 send 大致做这些事:

js 复制代码
var callbacks = new Map()

window.myascf = {
  send: function(action, params, options) {
    return new Promise(function(resolve, reject) {
      var requestId = createRequestId()

      var request = {
        requestId: requestId,
        action: action,
        params: params || {}
      }

      callbacks.set(requestId, {
        resolve: resolve,
        reject: reject,
        action: action,
        createdAt: Date.now()
      })

      window.MyASCFNative.postMessage(JSON.stringify(request))
    })
  }
}

这里有一个 callback map。

它的作用是保存每一次请求对应的 Promise 回调。

等 ArkTS 回来时,再根据 requestId 找到它。

4. ArkWeb 注入对象

ArkTS 侧通过 JavaScriptProxy 向 H5 注入一个对象:

ts 复制代码
Web({
  src: $rawfile('web/index.html'),
  controller: this.controller
})
.javaScriptProxy({
  object: this.nativeProxy,
  name: 'MyASCFNative',
  methodList: ['postMessage'],
  controller: this.controller
})

H5 侧就可以调用:

js 复制代码
window.MyASCFNative.postMessage(JSON.stringify(request))

这里的 postMessage 是 ArkTS 暴露出来的方法。

5. JavaScriptProxy 只做转发

JavaScriptProxy 不应该写业务逻辑。

它只负责把消息交给 BridgeController。

ts 复制代码
export class JavaScriptProxy {
  constructor(private bridgeController: BridgeController) {}

  postMessage(message: string): void {
    this.bridgeController.handleMessage(message)
  }
}

这是一条很重要的边界。

如果在 Proxy 里直接判断 action,那么它很快就会变得混乱。

Proxy 是入口,不是业务层。

6. ArkTS 返回响应

ArkTS 返回给 H5 的响应格式如下:

json 复制代码
{
  "requestId": "req_1700000000000_1",
  "code": 0,
  "message": "success",
  "data": {
    "echoAction": "ui.showToast"
  }
}

H5 暴露一个全局回调:

js 复制代码
window.__myascf_on_native_response__ = function(responseText) {
  var response = JSON.parse(responseText)
  var callback = callbacks.get(response.requestId)

  if (!callback) {
    console.warn('CALLBACK_LOST', response.requestId)
    return
  }

  callbacks.delete(response.requestId)

  if (response.code === 0) {
    callback.resolve(response)
  } else {
    callback.reject(response)
  }
}

ArkTS 通过 runJavaScript 调用这个函数。

7. 为什么要小心字符串转义

ArkTS 回调 H5 时,本质上是在执行一段 JS 字符串。

比如:

js 复制代码
window.__myascf_on_native_response__('{...}')

如果 JSON 里有引号、换行或特殊字符,脚本就可能拼坏。

所以项目里抽出了 BridgeCallbackExecutor,统一负责:

  • JSON 序列化;
  • 参数安全转义;
  • 拼接 JS;
  • 执行 runJavaScript;
  • 记录日志。

这样 BridgeController 就不需要关心字符串细节。

8. 这一阶段的意义

到这里,我们还没有实现真正的 Toast。

但是通信边界已经成立了:

text 复制代码
H5 Promise
→ requestId
→ callback map
→ JavaScriptProxy
→ BridgeController
→ runJavaScript
→ H5 Promise resolve / reject

这一步很重要。

因为后面的 Dispatcher、Registry、Biz、Imp 都建立在这个边界之上。

项目地址:

text 复制代码
https://github.com/lichenyang5/MiniAppRuntime-Harmony
相关推荐
mayaairi13 分钟前
Vue2 组件通讯(四):v-model、scoped样式、mixins与plugins
前端·javascript·vue.js
梦曦i24 分钟前
@meng-xi/uni-router 未来展望:夯实基础、深化体验、探索前沿
前端·uni-app
Yeyu24 分钟前
AAOS AppCard 实践:怎么把自己的 App 塞进别人的卡片里
前端
IT_陈寒1 小时前
Vite静态资源路径这个大坑害我调了一下午
前端·人工智能·后端
狗哥哥1 小时前
分享下我的读书清单
前端
合天网安实验室1 小时前
Log4J2 FilteredObjectInputStream RCE 漏洞分析
前端·黑客
两点王爷2 小时前
使用 GeoServer 发布 SHP 数据并在前端页面加载显示
前端
lhldsg2 小时前
全民健身解决方案软件开发实战:从架构设计到部署指南
java·前端·数据库·小程序
四六的六2 小时前
让 AI 自己去点后台页面,它把我们的库存点没了
前端·人工智能·agent·个人开发·ai编程·ai产品·ai前端
两点王爷2 小时前
Java 与前端加载 MVT 数据:从服务端切片到浏览器渲染
java·前端·状态模式