JSBridge实现原生native和网页的双向通信。作为原生和网页沟通的桥梁,JSBridge是通用兼容的,不会单独为一个平台开发一套逻辑。这里记录一下HarmonyOS NEXT中的通用JSBridge的实现。
原生实现
page.etc
javascript
import web_webview from '@ohos.web.webview'
import router from '@ohos.router'
import { JSBridgeBean } from '../data/JSBridgeBean';
import { deviceInfo } from '@kit.MDMKit';
@Entry
@Component
struct WebViewPage {
//网页标题
@State mTitle: string = (router.getParams() as Record<string, string>)['mTitle'];
//网页地址
@State mUrl: string = (router.getParams() as Record<string, string>)['mUrl'];
controller: web_webview.WebviewController = new web_webview.WebviewController()
build() {
Column() {
Web({ src: this.mUrl, controller: this.controller })
.width('100%')
.height('100%')
.onPageEnd(() => {
this.registerUnifiedJSBridge();
})
}.height('100%')
.width('100%')
}
registerUnifiedJSBridge() {
// 定义统一的 Bridge 对象
const JSBridge = {
// 网页调用此方法发起请求
invoke: (command: string, paramsJson: string, callbackId: string): void => {
console.log(`[JSBridge] Received command: ${command}, params: ${paramsJson}, cb: ${callbackId}`);
//TODO 埋点
try {
const params :JSBridgeBean = JSON.parse(paramsJson || '{}');
// 根据 command 分发处理
switch (command) {
case 'getDeviceInfo':
this.handleGetDeviceInfo(callbackId);
break;
case 'showToast':
this.handleShowToast(params.content, callbackId);
break;
case 'pickImage':
this.handlePickImage(callbackId);
break;
default:
this.sendCallback(callbackId, { success: false, error: 'Unknown command' });
}
} catch (e) {
console.error('[JSBridge] Parse params failed:', e);
this.sendCallback(callbackId, { success: false, error: 'Invalid params' });
}
},
// 原生通过此方法回调(网页需实现 window.JSBridge.onCallback)
onCallback: (callbackId: string, resultJson: string): void => {
// 实际上这个方法由原生调用网页,此处可留空或用于调试
console.log('[JSBridge] onCallback called (should be in web side)');
}
};
// 只暴露 JSBridge 对象,方法列表包含 invoke(onCallback 通常不被网页主动调用)
this.controller.registerJavaScriptProxy(
JSBridge,
'JSBridge',
['invoke'] // 注意:onCallback 是原生调网页,不需要在这里暴露给网页调用
);
}
// 功能实现:获取设备信息
handleGetDeviceInfo(callbackId: string): void {
//TODO
const deviceInfo:DeviceInfo = new DeviceInfo("","","")
this.sendCallback(callbackId, { success: true, data: deviceInfo });
}
// 功能实现:显示 Toast
handleShowToast(message: string, callbackId: string): void {
// TODO 实际可调用 @ohos.promptAction
this.sendCallback(callbackId, { success: true, message: 'Toast shown' });
}
// 功能实现:选择图片(示例,需集成媒体库)
handlePickImage(callbackId: string): void {
// TODO 模拟异步操作
setTimeout(() => {
this.sendCallback(callbackId, {
success: true,
data: { uri: 'file://image1.jpg' }
});
}, 800);
}
// 统一回调方法:调用网页的 window.JSBridge.onCallback
sendCallback(callbackId: string, result: any): void {
const resultStr = JSON.stringify(result);
const jsCode = `typeof window.JSBridge?.onCallback === 'function' ?
window.JSBridge.onCallback(${JSON.stringify(callbackId)}, ${resultStr}) :
console.warn('JSBridge.onCallback not found')`;
this.controller.callJavaScript(jsCode, (error) => {
if (error) {
console.error('[JSBridge] Callback failed:', error);
//TODO 埋点
}
});
}
}
网页实现
html
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8" />
<title>Unified JSBridge</title>
</head>
<body>
<button onclick="getDeviceInfo()">获取设备信息</button>
<button onclick="showMyToast()">显示原生提示</button>
<button onclick="selectImage()">选择图片</button>
<div id="output"></div>
<script>
// 全局回调映射表(避免 callbackId 冲突)
const callbacks = new Map();
// 统一调用方法
function invoke(command, params = {}) {
return new Promise((resolve, reject) => {
if (!window.JSBridge) {
reject(new Error('JSBridge not available'));
return;
}
const callbackId = 'cb_' + Date.now() + '_' + Math.random().toString(36).slice(2);
callbacks.set(callbackId, { resolve, reject });
window.JSBridge.invoke(command, JSON.stringify(params), callbackId);
});
}
// 原生回调入口(必须挂载到 window.JSBridge)
window.JSBridge = window.JSBridge || {};
window.JSBridge.onCallback = function (callbackId, result) {
const cb = callbacks.get(callbackId);
if (cb) {
callbacks.delete(callbackId);
if (result.success) {
cb.resolve(result.data || result);
} else {
cb.reject(new Error(result.error || 'Operation failed'));
}
}
};
// 使用示例
async function getDeviceInfo() {
try {
const info = await invoke('getDeviceInfo');
document.getElementById('output').innerText = JSON.stringify(info, null, 2);
} catch (e) {
alert('Error: ' + e.message);
}
}
async function showMyToast() {
await invoke('showToast', { message: 'Hello from Web!' });
alert('Toast sent to native');
}
async function selectImage() {
const result = await invoke('pickImage');
console.log('Selected image:', result.uri);
}
</script>
</body>
</html>