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