// WebViewBridge.java
public class WebViewBridge {
private WeakReference<WebView> mWebViewRef;
private String mBridgeName;
public WebViewBridge(WebView webView, String bridgeName) {
mWebViewRef = new WeakReference<>(webView);
mBridgeName = bridgeName;
webView.addJavascriptInterface(this, bridgeName);
}
@JavascriptInterface
public void postMessage(String targetBridge, String message) {
MainActivity activity = (MainActivity) mWebViewRef.get().getContext();
activity.routeMessage(targetBridge, message);
}
public void receiveMessage(String message) {
WebView webView = mWebViewRef.get();
if (webView != null) {
String js = String.format("window.%s.onMessage('%s')",
mBridgeName, message);
webView.evaluateJavascript(js, null);
}
}
}
// MainActivity.java
public class MainActivity extends AppCompatActivity {
private WebView webViewA, webViewB;
private WebViewBridge bridgeA, bridgeB;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
webViewA = findViewById(R.id.webview_a);
webViewB = findViewById(R.id.webview_b);
// 初始化桥接
bridgeA = new WebViewBridge(webViewA, "BridgeA");
bridgeB = new WebViewBridge(webViewB, "BridgeB");
// 加载HTML
webViewA.loadUrl("file:///android_asset/webview_a.html");
webViewB.loadUrl("file:///android_asset/webview_b.html");
}
// 消息路由方法
public void routeMessage(String targetBridge, String message) {
if ("BridgeA".equals(targetBridge)) {
bridgeA.receiveMessage(message);
} else if ("BridgeB".equals(targetBridge)) {
bridgeB.receiveMessage(message);
}
}
}
// 启用JavaScript
webView.getSettings().setJavaScriptEnabled(true);
// 防止内存泄漏
webView.setWebViewClient(new WebViewClient());
webView.setWebChromeClient(new WebChromeClient());
// 文件访问权限
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN) {
webView.getSettings().setAllowFileAccessFromFileURLs(true);
webView.getSettings().setAllowUniversalAccessFromFileURLs(true);
}
// WebViewBridge.swift
class WebViewBridge: NSObject, WKScriptMessageHandler {
private weak var webView: WKWebView?
private let bridgeName: String
init(webView: WKWebView, bridgeName: String) {
self.webView = webView
self.bridgeName = bridgeName
super.init()
webView.configuration.userContentController.add(self, name: bridgeName)
}
func userContentController(_ controller: WKUserContentController,
didReceive message: WKScriptMessage) {
guard let body = message.body as? [String: Any],
let target = body["target"] as? String,
let msg = body["message"] as? String else {
return
}
if let delegate = UIApplication.shared.delegate as? AppDelegate {
delegate.routeMessage(target: target, message: msg)
}
}
func sendMessage(_ message: String) {
let js = "window.\(bridgeName).onMessage('\(message)')"
webView?.evaluateJavaScript(js, completionHandler: nil)
}
}
// AppDelegate.swift
@UIApplicationMain
class AppDelegate: UIResponder, UIApplicationDelegate {
var window: UIWindow?
var webViewA: WKWebView!
var webViewB: WKWebView!
var bridgeA: WebViewBridge!
var bridgeB: WebViewBridge!
func application(_ application: UIApplication,
didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?) -> Bool {
// 创建WebView
webViewA = WKWebView(frame: .zero)
webViewB = WKWebView(frame: .zero)
// 初始化桥接
bridgeA = WebViewBridge(webView: webViewA, bridgeName: "BridgeA")
bridgeB = WebViewBridge(webView: webViewB, bridgeName: "BridgeB")
// 加载HTML
if let urlA = Bundle.main.url(forResource: "webview_a", withExtension: "html") {
webViewA.loadFileURL(urlA, allowingReadAccessTo: urlA)
}
if let urlB = Bundle.main.url(forResource: "webview_b", withExtension: "html") {
webViewB.loadFileURL(urlB, allowingReadAccessTo: urlB)
}
return true
}
// 消息路由
func routeMessage(target: String, message: String) {
if target == "BridgeA" {
bridgeA.sendMessage(message)
} else if target == "BridgeB" {
bridgeB.sendMessage(message)
}
}
}
// HarmonyBridge.java
public class HarmonyBridge {
private WebView webView;
private String bridgeName;
private Context context;
public HarmonyBridge(Context context, WebView webView, String bridgeName) {
this.context = context;
this.webView = webView;
this.bridgeName = bridgeName;
this.webView.addJsInterface(this, bridgeName);
}
@JSFunction
public void postMessage(String targetBridge, String message) {
// 获取主Ability
MainAbility mainAbility = (MainAbility) context;
mainAbility.routeMessage(targetBridge, message);
}
public void receiveMessage(String message) {
String js = "window." + bridgeName + ".onMessage('" + message + "')";
webView.executeJs(js);
}
}
// MainAbility.java
public class MainAbility extends Ability {
private WebView webViewA;
private WebView webViewB;
private HarmonyBridge bridgeA;
private HarmonyBridge bridgeB;
@Override
public void onStart(Intent intent) {
super.onStart(intent);
DirectionalLayout layout = new DirectionalLayout(this);
// 创建WebViewA
webViewA = new WebView(this);
webViewA.setWidth(ComponentContainer.LayoutConfig.MATCH_PARENT);
webViewA.setHeight(ComponentContainer.LayoutConfig.MATCH_CONTENT);
webViewA.setWeight(1);
webViewA.load("file:///assets/webview_a.html");
// 创建WebViewB
webViewB = new WebView(this);
webViewB.setWidth(ComponentContainer.LayoutConfig.MATCH_PARENT);
webViewB.setHeight(ComponentContainer.LayoutConfig.MATCH_CONTENT);
webViewB.setWeight(1);
webViewB.load("file:///assets/webview_b.html");
// 初始化桥接
bridgeA = new HarmonyBridge(this, webViewA, "BridgeA");
bridgeB = new HarmonyBridge(this, webViewB, "BridgeB");
// 添加到布局
layout.addComponent(webViewA);
layout.addComponent(webViewB);
setUIContent(layout);
}
// 消息路由
public void routeMessage(String targetBridge, String message) {
if ("BridgeA".equals(targetBridge)) {
bridgeA.receiveMessage(message);
} else if ("BridgeB".equals(targetBridge)) {
bridgeB.receiveMessage(message);
}
}
}
// 启用JavaScript
WebConfig webConfig = webView.getWebConfig();
webConfig.setJavaScriptPermit(true);
// 设置WebView代理
webView.setWebAgent(new WebAgent() {
@Override
public boolean onPageStart(WebView webView, String url) {
// 页面开始加载处理
return true;
}
});
// bridge-sdk.js
class CrossWebViewBridge {
constructor(bridgeName) {
this.bridgeName = bridgeName;
this.messageHandlers = {};
}
postMessage(targetBridge, message) {
// Android
if (window.AndroidBridge) {
window.AndroidBridge.postMessage(targetBridge, message);
}
// iOS
else if (window.webkit && window.webkit.messageHandlers[this.bridgeName]) {
window.webkit.messageHandlers[this.bridgeName].postMessage({
target: targetBridge,
message: message
});
}
// HarmonyOS
else if (window.HarmonyBridge) {
window.HarmonyBridge.postMessage(targetBridge, message);
}
}
onMessage(handler) {
this.messageHandlers['default'] = handler;
}
// 供原生调用的方法
__onNativeMessage(message) {
if (this.messageHandlers['default']) {
this.messageHandlersmessage;
}
}
}
// 初始化
window.BridgeA = new CrossWebViewBridge('BridgeA');
window.BridgeB = new CrossWebViewBridge('BridgeB');
DOCTYPE html>
<html>
<head>
<title>WebView Atitle>
<script src="bridge-sdk.js">script>
head>
<body>
<button onclick="sendMessage()">发送消息到WebView Bbutton>
<script>
// 初始化桥接
const bridgeA = new CrossWebViewBridge('BridgeA');
// 注册消息处理器
bridgeA.onMessage(function(message) {
console.log('WebViewA收到消息:', message);
document.getElementById('output').innerText = message;
});
// 发送消息
function sendMessage() {
bridgeA.postMessage('BridgeB', 'Hello from WebView A');
}
script>
<div id="output">div>
body>
html>
| 功能 | Android | iOS | 鸿蒙 | 解决方案 |
|---|---|---|---|---|
| JS接口注入 | addJavascriptInterface |
WKUserContentController |
addJsInterface |
统一桥接SDK |
| JS执行方式 | evaluateJavascript |
evaluateJavaScript |
executeJs |
封装原生方法 |
| 文件访问 | 需权限配置 | 沙盒限制 | 资源目录访问 | 使用相对路径 |
| 内存管理 | WeakReference |
weak var |
自动回收 | 弱引用处理 |
| 后台通信 | Service支持 |
后台限制 | ServiceAbility |
消息队列缓存 |
// 在WebView A中
bridgeA.onMessage(function(message) {
console.log('实时消息:', message);
// 立即回复
bridgeA.postMessage('BridgeB', '收到消息');
});
// 在WebView B中
setInterval(() => {
bridgeB.postMessage('BridgeA', `心跳 ${Date.now()}`);
}, 5000);
// Android 文件传输
@JavascriptInterface
public void sendFile(String base64Data, String fileName) {
byte[] data = Base64.decode(base64Data, Base64.DEFAULT);
// 保存文件
File file = new File(getFilesDir(), fileName);
try (FileOutputStream fos = new FileOutputStream(file)) {
fos.write(data);
}
// 通知目标WebView
String message = "file://" + file.getAbsolutePath();
routeMessage("BridgeB", message);
}
// Android 源验证
private boolean isValidOrigin(String origin) {
return Arrays.asList(
"file:///android_asset/",
"https://trusted-domain.com"
).contains(origin);
}
// 在WebViewClient中
@Override
public boolean shouldOverrideUrlLoading(WebView view, WebResourceRequest request) {
if (!isValidOrigin(request.getUrl().toString())) {
return true; // 阻止加载
}
return false;
}
class BridgeLogger {
constructor(bridgeName) {
this.bridgeName = bridgeName;
}
log(message, level = 'info') {
// 发送日志到原生
const payload = {
type: 'log',
level: level,
message: message
};
this.bridge.postMessage('Logger', JSON.stringify(payload));
}
}
// 集成到SDK
CrossWebViewBridge.prototype.log = function(message) {
this.logger.log(message);
};
// Android 性能监控
private void startPerformanceMonitoring() {
new Timer().scheduleAtFixedRate(new TimerTask() {
@Override
public void run() {
Debug.MemoryInfo memoryInfo = new Debug.MemoryInfo();
Debug.getMemoryInfo(memoryInfo);
long totalMemory = memoryInfo.getTotalPss();
int cpuUsage = getCpuUsage();
String message = String.format(
"{\"memory\":%d,\"cpu\":%d}",
totalMemory, cpuUsage
);
bridgeA.postMessage("Monitor", message);
}
}, 0, 5000); // 每5秒监控一次
}
消息协议标准化
{
"version": "1.0",
"id": "uuid",
"timestamp": 1685091200,
"source": "BridgeA",
"target": "BridgeB",
"type": "text/json/file",
"payload": {}
}
错误处理机制
try {
bridgeA.postMessage("BridgeB", largeData);
} catch (e) {
if (e.message.includes("Message too long")) {
// 分片发送
sendInChunks(largeData);
}
}
心跳保活
// Android心跳服务
public class HeartbeatService extends Service {
@Override
public int onStartCommand(Intent intent, int flags, int startId) {
new Thread(() -> {
while (true) {
if (webViewA != null) {
webViewA.post(() ->
webViewA.evaluateJavascript("BridgeA.ping()", null)
);
}
Thread.sleep(30000);
}
}).start();
return START_STICKY;
}
}
通过以上方案,可在Android、iOS和鸿蒙平台上实现高效稳定的WebView间通信。关键点在于:
实际部署时建议: