HarmonyOS鸿蒙Next应用如何实现原生和H5双向通信?请提供详细集成步骤介绍和源码
HarmonyOS鸿蒙Next应用如何实现原生和H5双向通信?请提供详细集成步骤介绍和源码 在 ArkUI 开发中,Web 组件(Web)允许开发者在应用内嵌入网页,实现混合开发场景。
那鸿蒙应用如何实现原生和H5双向通信?请提供详细集成步骤介绍和源码
一、双向通信实现原理
1、双向通信概念 Web 到 ArkUI(反向通信)通过registerJavaScriptProxy将 ArkUI 对象注册到网页的window对象,允许网页通过window.xxx调用 ArkUI 暴露的方法。
ArkUI 到 Web(正向通信)通过runJavaScript执行网页 JS 代码,支持回调获取返回值,实现原生代码调用网页函数。
2、双向通信流程图 ArkUI Web ┌──────────────┐ ┌──────────────┐ │ registerJS ├─────────────▶ window.objName │ │ (反向注册) │ ├──────────────┤ ├──────────────┤ │ call test() │ │ runJavaScript├─────────────▶ execute JS code │ │ (正向调用) │ ├──────────────┤ └──────────────┘ └──────────────┘
二、双向通信实现步骤
1、ArkUI 定义可被网页调用的对象 创建一个TestObj类,声明允许网页调用的方法(白名单机制):
class TestObj {
// 网页可调用的方法1:返回字符串
test(): string {
return "ArkUI Web Component";
}
// 网页可调用的方法2:打印日志
toString(): void {
console.log('Web Component toString');
}
// 网页可调用的方法3:接收网页消息
receiveMessageFromWeb(message: string): void {
console.log(`Received from web: ${message}`);
}
}
2、ArkUI 组件核心代码 初始化控制器与状态
@Entry
@Component
struct WebComponent {
// Webview控制器
controller: webview.WebviewController = new webview.WebviewController();
// 注册到网页的ArkUI对象
@State testObj: TestObj = new TestObj();
// 注册名称(网页通过window.[name]访问)
@State regName: string = 'objName';
// 接收网页返回数据
@State webResult: string = '';
build() { /* 组件布局与交互逻辑 */ }
}
布局与交互按钮,添加三个核心功能按钮:
Column() {
// 显示网页返回数据
Text(`Web返回数据:${this.webResult}`).fontSize(16).margin(10);
// 1. 注册ArkUI对象到网页
Button('注册到Window')
.onClick(() => {
this.controller.registerJavaScriptProxy(
this.testObj, // ArkUI对象
this.regName, // 网页访问名称
["test", "toString", "receiveMessageFromWeb"] // 允许调用的方法白名单
);
})
// 2. ArkUI调用网页JS
Button('调用网页函数')
.onClick(() => {
this.controller.runJavaScript(
'webFunction("Hello from ArkUI!")', // 执行网页JS代码
(error, result) => { // 回调处理返回值
if (!error) this.webResult = result || '无返回值';
}
);
})
// 3. Web组件加载
Web({ src: $rawfile('index.html'), controller: this.controller })
.javaScriptAccess(true) // 开启JS交互权限
.onPageEnd(() => { // 页面加载完成时触发
// 页面加载后自动调用网页测试函数
this.controller.runJavaScript('initWebData()');
})
}
3. registerJavaScriptProxy的实际作用是,将 ArkUI 对象绑定到网页window,实现反向通信。
registerJavaScriptProxy(
obj: Object, // ArkUI中定义的对象
name: string, // 网页访问的名称(如window.name)
methods: string[] // 允许调用的方法白名单(严格匹配方法名)
);
三、源码示例

WebViewPage.ets
import { webview } from '@kit.ArkWeb';
import { BusinessError } from '@kit.BasicServicesKit';
class TestObj {
constructor() {
}
test(): string {
return "ArkUI Web Component";
}
toString(): void {
console.log('Web Component toString');
}
receiveMessageFromWeb(message: string): void {
console.log(`Received message from web: ${message}`);
}
}
@Entry
@Component
struct WebViewPage {
controller: webview.WebviewController = new webview.WebviewController();
@State testObjtest: TestObj = new TestObj();
@State name: string = 'objName';
@State webResult: string = '';
build() {
Column() {
Text(this.webResult).fontSize(20)
Button('refresh')
.onClick(() => {
try {
this.controller.refresh();
} catch (error) {
console.error(`ErrorCode: ${(error as BusinessError).code}, Message: ${(error as BusinessError).message}`);
}
})
Button('Register JavaScript To Window')
.onClick(() => {
try {
this.controller.registerJavaScriptProxy(this.testObjtest, this.name, ["test", "toString", "receiveMessageFromWeb"]);
} catch (error) {
console.error(`ErrorCode: ${(error as BusinessError).code}, Message: ${(error as BusinessError).message}`);
}
})
Button('deleteJavaScriptRegister')
.onClick(() => {
try {
this.controller.deleteJavaScriptRegister(this.name);
} catch (error) {
console.error(`ErrorCode: ${(error as BusinessError).code}, Message: ${(error as BusinessError).message}`);
}
})
Button('Send message to web')
.onClick(() => {
try {
this.controller.runJavaScript(
'receiveMessageFromArkUI("Hello from ArkUI!")',
(error, result) => {
if (error) {
console.error(`run JavaScript error, ErrorCode: ${(error as BusinessError).code}, Message: ${(error as BusinessError).message}`);
return;
}
console.info(`Message sent to web result: ${result}`);
}
);
} catch (error) {
console.error(`ErrorCode: ${(error as BusinessError).code}, Message: ${(error as BusinessError).message}`);
}
})
Button('Get data from web')
.onClick(() => {
try {
this.controller.runJavaScript(
'getWebPageData()',
(error, result) => {
if (error) {
console.error(`run JavaScript error, ErrorCode: ${(error as BusinessError).code}, Message: ${(error as BusinessError).message}`);
return;
}
if (result) {
this.webResult = result;
console.info(`Data from web: ${result}`);
}
}
);
} catch (error) {
console.error(`ErrorCode: ${(error as BusinessError).code}, Message: ${(error as BusinessError).message}`);
}
})
Web({ src: $rawfile('index.html'), controller: this.controller })
.javaScriptAccess(true)
.onPageEnd(e => {
try {
this.controller.runJavaScript(
'test()',
(error, result) => {
if (error) {
console.error(`run JavaScript error, ErrorCode: ${(error as BusinessError).code}, Message: ${(error as BusinessError).message}`);
return;
}
if (result) {
this.webResult = result;
console.info(`The test() return value is: ${result}`);
}
}
);
if (e) {
console.info('url: ', e.url);
}
} catch (error) {
console.error(`ErrorCode: ${(error as BusinessError).code}, Message: ${(error as BusinessError).message}`);
}
})
.width("100%")
.height("50%")
}
.width("100%")
.height("100%")
.backgroundColor(Color.Black)
}
}
index.html
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Web Communication</title>
</head>
<body>
<button onclick="callArkUIMethod()">Call ArkUI Method</button>
<button onclick="sendMessageToArkUI()">Send message to ArkUI</button>
<div id="messageFromArkUI"></div>
<script>
function callArkUIMethod() {
const result = window.objName.test();
console.log("Result from ArkUI: ", result);
}
function sendMessageToArkUI() {
window.objName.receiveMessageFromWeb('Hello from web!');
}
function receiveMessageFromArkUI(message) {
const messageDiv = document.getElementById('messageFromArkUI');
messageDiv.textContent = `Received message from ArkUI: ${message}`;
}
function getWebPageData() {
return "Data from web page";
}
function test() {
return "Test function result from web";
}
</script>
</body>
</html>
注意
组件销毁时调用deleteJavaScriptRegister(name)取消注册,避免内存泄漏:
onDestroy() {
this.controller.deleteJavaScriptRegister(this.regName);
}
更多关于HarmonyOS鸿蒙Next应用如何实现原生和H5双向通信?请提供详细集成步骤介绍和源码的实战系列教程也可以访问 https://www.itying.com/category-93-b0.html
鸿蒙Next应用通过Web组件实现原生与H5双向通信。
原生调用H5:
- 使用
webController.runJavaScript()执行JS方法。 - H5通过
window.ohosWebView接收消息。
H5调用原生:
- 注册JavaScriptProxy对象到Web组件。
- H5通过
window.ohosJavaScriptProxy调用原生方法。
集成步骤:
- 创建Web组件并加载H5页面。
- 实现JavaScriptProxy接口供H5调用。
- 通过
runJavaScript调用H5函数。
关键代码:
// 注册JavaScriptProxy
webController.registerJavaScriptProxy({
callNativeMethod: (msg: string) => {
// 处理H5消息
}
}, 'bridge');
// H5调用:window.ohosJavaScriptProxy.bridge.callNativeMethod('data')
// 原生调用H5:webController.runJavaScript("window.receiveMsg('data')")
双向通信需在Web组件加载完成后建立连接。
在HarmonyOS Next中,通过ArkUI的Web组件实现原生与H5双向通信,主要依赖WebController与WebMessagePort。以下是核心步骤与示例代码:
一、 核心机制
- H5向原生通信:使用
WebMessagePort建立消息端口,或通过WebController执行JavaScript。 - 原生向H5通信:通过
WebController的runJavaScript方法注入并执行JS脚本。
二、 详细集成步骤与源码
步骤1:创建Web组件并加载H5页面
import webview from '@ohos.web.webview';
import { webviewController } from '@ohos.web.webview';
@Entry
@Component
struct Index {
private webController: webview.WebviewController = new webview.WebviewController();
build() {
Column() {
// 创建Web组件
Web({ src: 'https://www.example.com', controller: this.webController })
.width('100%')
.height('100%')
}
}
}
步骤2:H5向原生发送消息(使用WebMessagePort)
原生侧:
// 在Web组件加载完成后建立消息端口
private webMessagePort: webview.WebMessagePort[] = [];
aboutToAppear() {
// 获取消息端口
this.webMessagePort = this.webController.getMessagePorts();
// 监听H5发送的消息
this.webMessagePort[0].onMessageEvent((message: webview.WebMessage) => {
console.info('收到H5消息:', message.getData());
// 处理消息并回复
this.webMessagePort[0].postMessageEvent('原生已收到消息');
});
}
H5侧:
<script>
// 向原生发送消息
window.onload = function() {
if (window.chrome && chrome.webview) {
const port = chrome.webview.getMessagePort();
port.postMessage('Hello from H5');
// 监听原生回复
port.onmessage = function(event) {
console.log('收到原生回复:', event.data);
};
}
};
</script>
步骤3:原生向H5发送消息(执行JavaScript)
// 通过WebController执行JS代码
this.webController.runJavaScript('receiveNativeMessage("原生发送的数据")');
H5侧需定义接收函数:
<script>
function receiveNativeMessage(data) {
console.log('收到原生消息:', data);
// 处理数据
}
</script>
步骤4:原生调用H5函数并获取返回值
this.webController.runJavaScript('getH5Data()', (error, result) => {
if (!error) {
console.info('H5返回结果:', result);
}
});
三、 关键配置
在module.json5中配置网络权限:
{
"module": {
"requestPermissions": [
{
"name": "ohos.permission.INTERNET"
}
]
}
}
四、 注意事项
- 同源策略:H5页面需与原生应用保持同源或配置跨域支持。
- 生命周期管理:消息端口需在组件销毁时关闭。
- 数据类型:通信数据支持字符串、基本类型及可序列化对象。
以上方案实现了完整的双向通信链路,可根据实际业务需求扩展消息协议。

