ArkWeb实战学习笔记04-JavaScript与Native通信
Hybrid开发中,Native与Web页面间的双向通信是核心需求。ArkWeb(方舟Web)提供JavaBridge(通过javaScriptProxy)和Proxy双向通信(通过WebMessagePort)两种方式。本文基于HarmonyOS API 9+,演示Native调用JS、JS回调Native以及双向消息传递的完整实现步骤,附带代码和关键注意事项。
核心概念
ArkWeb框架下,JavaScript与Native通信依赖以下机制:
- JavaBridge :通过
Web组件的javaScriptProxy()方法注册一个JS Bridge对象。Native可以直接调用JS方法(@NativeCall),JS也可以通过该对象来回调Native方法。 - WebMessagePort :基于
createWebMessagePorts()创建一对端口(Native端与JS端),通过postMessage()和onMessage()实现双向数据传递,不依赖JS Bridge对象。
官方文档中提及的"Proxy双向通信"即指WebMessagePort通道。通信数据均为字符串,复杂对象需要手动序列化。
环境准备
在DevEco Studio中创建工程并引入ArkWeb组件。确保module.json5中声明权限(如ohos.permission.INTERNET),并配置compileSdkVersion为9或以上。当前(2024年)推荐API 10,因为WebMessagePort在API 10中有更完善的支持。
⚠️ 注意:若使用API 9,
createWebMessagePorts()返回的类型与API 10略有差异,建议统一使用API 10。
核心实现
以下代码基于HarmonyOS官方API文档编写,测试环境为DevEco Studio 4.0 + API 10。所有示例均嵌入在EntryAbility或自定义WebComponent中。
1. Native调用JavaScript
Native侧通过webview.runJavaScript()直接执行JS代码。适用于简单调用,无需返回值或异步处理。
Native侧代码(ts):
typescript
import { webview } from '@kit.ArkWeb';
// 获取Web控制器
@Entry
@Component
struct WebPage {
private controller: webview.WebviewController = new webview.WebviewController();
build() {
Column() {
Button('Native调用JS: 修改标题')
.onClick(() => {
// 调用JS方法,无返回值
this.controller.runJavaScript("document.title = 'Hello from Native';");
})
Web({ src: $rawfile('index.html'), controller: this.controller })
.javaScriptAccess(true)
}
}
}
JS侧(index.html): 无需特殊处理,document.title直接响应。
⚠️ 注意:
runJavaScript不能同步接收返回值。如需获取JS执行结果,需要搭配javaScriptProxy或使用evaluateJavaScript(API 10新增,但本文不展开)。
2. JavaScript回调Native
通过javaScriptProxy()注册一个对象到JS环境,JS可以调用该对象上的同步方法。
Native侧代码:
typescript
import { webview } from '@kit.ArkWeb';
class JsBridge {
// 此方法必须使用@NativeCall装饰(API 10支持)
@NativeCall
public onJsCall(message: string): string {
console.log('Native收到JS消息: ' + message);
return 'Native已收到: ' + message;
}
}
@Entry
@Component
struct WebPage {
private controller: webview.WebviewController = new webview.WebviewController();
aboutToAppear() {
// 注册JS Bridge,名称任意,JS访问时用该名称
this.controller.javaScriptProxy({
object: new JsBridge(),
name: 'nativeBridge',
methodList: ['onJsCall'],
// 控制JS线程是否同步等待Native返回
syncCall: true // 若不需要返回值可设为false
});
}
build() {
Column() {
Button('激活JS回调')
.onClick(() => {
// 让JS主动调用nativeBridge.onJsCall
this.controller.runJavaScript("nativeBridge.onJsCall('来自JS的问候'); alert('结果: ' + result);");
})
Web({ src: $rawfile('index.html'), controller: this.controller })
.javaScriptAccess(true)
}
}
}
JS侧(index.html):
html
<script>
// 在页面加载完成后,可以通过nativeBridge对象直接调用
window.onload = function() {
// 测试:点击按钮后Native注册的Bridge已经可用
var btn = document.getElementById('btn');
btn.onclick = function() {
var ret = nativeBridge.onJsCall('Hello Native!');
console.log('JS收到Native返回: ' + ret);
};
};
</script>
⚠️ 注意:
javaScriptProxy必须在Web组件加载页面之前调用(在aboutToAppear或页面初始化时),否则JS侧可能找不到对象。syncCall设置为true时,JS会阻塞等待Native方法返回,容易导致性能问题,建议仅在必须同步获取结果时使用。
3. Proxy双向通信(基于WebMessagePort)
WebMessagePort提供更灵活的双向通道,不依赖对象注册,适合大量消息或跨页面通信。
Native侧代码:
typescript
import { webview } from '@kit.ArkWeb';
@Entry
@Component
struct WebPage {
private controller: webview.WebviewController = new webview.WebviewController();
private nativePort: webview.WebMessagePort | null = null;
aboutToAppear() {
// 创建一对端口,nativePort和jsPort
let ports = this.controller.createWebMessagePorts();
this.nativePort = ports[0];
// 将jsPort发送给Web页面(目标URL可以使用通配符*)
this.controller.postWebMessage(
'bridge-init',
[ports[1]],
'*'
);
// 监听Native端消息
this.nativePort.on('message', (event) => {
console.log('Native收到消息: ' + event.data);
// 回复消息
this.nativePort?.postMessage('Native已收到: ' + event.data);
});
}
build() {
Column() {
Button('发送消息给JS')
.onClick(() => {
this.nativePort?.postMessage('Hello from Native!');
})
Web({ src: $rawfile('index.html'), controller: this.controller })
.javaScriptAccess(true)
}
}
}
JS侧(index.html):
html
<script>
var jsPort;
// 监听来自Native的端口传递事件
window.addEventListener('message', function(event) {
if (event.data === 'bridge-init') {
// 取出端口对象
jsPort = event.ports[0];
// 设置JS端消息监听
jsPort.onmessage = function(e) {
console.log('JS收到消息: ' + e.data);
// 回复
jsPort.postMessage('JS已收到: ' + e.data);
};
}
});
</script>
⚠️ 注意:
postWebMessage的第二个参数是端口数组,第三个参数targetOrigin建议使用具体域名或*(仅调试)。生产环境中务必限定targetOrigin,避免消息泄露。WebMessagePort通信是异步的,不适合高频实时请求。

注意事项
- 数据大小限制:
postMessage传递的字符串长度在不同系统中可能有限制(官方建议单条消息不超过1MB),超长数据需分块或使用Blob等方案。 - 线程模型:Native调用JS在UI线程执行,长时间阻塞会导致页面卡顿。
javaScriptProxy的syncCall为true时尤其小心。 - 生命周期:Web组件销毁后,
WebMessagePort和javaScriptProxy自动失效。若页面重新加载,需要重新注册。 - 安全性:
javaScriptAccess设置为false会阻止所有JS执行,需根据场景灵活控制。另需注意不要将敏感Native对象暴露给JS。
上一篇 介绍了ArkWeb的基本页面加载与生命周期管理。下一篇将聚焦Web组件的安全策略与权限管理。
关于JavaBridge和Proxy通信还有哪些坑?欢迎在评论区交流实际踩过的版本兼容问题。