# ArkWeb 手记 03|把 JSBridge 做成协议
HarmonyOS 7 · ArkWeb 混合应用开发手记 03
上一篇已经把 H5 和 ArkTS
的双向调用跑通了。真正进业务以后,问题很快就从"能不能调用"变成"调用多了以后怎么不乱"。分享、登录、支付、定位、设备信息一起进来,如果每个方法都有自己的参数和回调,Bridge
很快会变成一团线。
这一篇不继续加能力,先把通信协议定下来。核心只有四个词:method、requestId、params、统一响应。
1. 为什么需要 requestId
H5
连续发两个异步请求时,返回顺序不一定和发出顺序一样。最稳的做法,是每次请求都带一个唯一
ID:
interface BridgeRequest {
requestId: string
method: string
params: string
}
interface BridgeResponse {
requestId: string
code: number
message: string
data: string
}
requestId 就像快递单号。结果回来以后,不需要猜它属于哪个请求。
【配图 01:BridgeRequest / BridgeResponse 协议代码,红圈标 requestId】
2. H5 侧只保留一个入口
不要继续增加 getToken()、openShare()、getLocation()
这种全局入口。可以把调用收成:
function callNative(method, params = {}) {
const requestId = `${Date.now()}_${Math.random()}`
return new Promise((resolve, reject) => {
pending.set(requestId, { resolve, reject })
NativeBridge.invoke(JSON.stringify({
requestId,
method,
params
}))
})
}
业务侧就很舒服:
const result = await callNative('getAppInfo')
3. ArkTS 做统一分发
原生只暴露一个 invoke:
invoke(raw: string): void {
try {
const request: BridgeRequest = JSON.parse(raw)
this.dispatch(request)
} catch (e) {
console.error('[Bridge] invalid request')
}
}
再按 method 分发:
private dispatch(req: BridgeRequest): void {
switch (req.method) {
case 'getAppInfo':
this.getAppInfo(req)
break
case 'openShare':
this.openShare(req)
break
default:
this.reply(req.requestId, 404, 'method not found', '')
}
}
这里宁可多写一个 switch,也不要动态执行任意方法名。Bridge
是能力边界,不是万能反射器。
4. 返回结构必须统一
成功和失败都走同一个结构:
private reply(id: string, code: number,
message: string, data: string): void {
const result: BridgeResponse = {
requestId: id,
code,
message,
data
}
this.sendToH5(result)
}
H5 收到后只处理一次:
window.onNativeMessage = function (result) {
const task = pending.get(result.requestId)
if (!task) return
pending.delete(result.requestId)
if (result.code === 0) {
task.resolve(result.data)
} else {
task.reject(new Error(result.message))
}
}

5. Promise 不是重点,超时才是
最容易漏的是:原生如果永远不回,Promise 就永远 pending。
const timer = setTimeout(() => {
pending.delete(requestId)
reject(new Error(`Bridge timeout: ${method}`))
}, 10000)
收到结果时记得 clearTimeout(timer)。10 秒不是标准值,要按业务调整。
6. 错误码不要随手写
至少分清:成功、参数错误、方法不存在、业务失败、超时。
enum BridgeCode {
SUCCESS = 0,
INVALID_PARAMS = 400,
METHOD_NOT_FOUND = 404,
BUSINESS_ERROR = 500
}
别今天返回 -1,明天返回 false,后天又返回字符串
"error"。这种协议最难维护。
7. 给协议加版本
interface BridgeRequest {
version: string
requestId: string
method: string
params: string
}
H5 和 App
不一定同时发版。版本字段不是为了显得专业,而是给以后兼容留出口。
8. 日志也按 requestId 串起来
[Bridge][REQ][req_1001] getAppInfo
[Bridge][RES][req_1001] code=0
线上排查时,只搜一个 ID 就能看到完整链路。
9. 不要把敏感能力直接暴露
支付、账号、文件、定位都应该有白名单和参数校验。Bridge 收到 method
不等于一定执行。
private allowedMethods: Set<string> =
new Set(['getAppInfo', 'openShare'])
来源、页面、登录态和业务权限需要按项目继续校验。
10. 这一篇真正得到什么
到这里,JSBridge 从"几个能调用的方法"变成了一条协议:请求有
ID,响应有固定结构,异步有超时,能力有白名单,日志可以串联。
下一篇我们处理另一个很真实的问题:H5
连续跳了三层以后,用户按返回,到底应该退网页还是退 ArkUI 页面?
更多推荐



所有评论(0)