移动端多 WebView 互访方案:Android、iOS 与鸿蒙实现
- [移动端多 WebView 互访方案:Android、iOS 与鸿蒙实现](#移动端多 WebView 互访方案:Android、iOS 与鸿蒙实现)
-
- 一、核心架构设计
- [二、Android 平台实现](#二、Android 平台实现)
-
- [1. 基础通信架构](#1. 基础通信架构)
- [2. 控制器实现](#2. 控制器实现)
- [3. WebView 安全配置](#3. WebView 安全配置)
- [三、iOS 平台实现 (Swift)](#三、iOS 平台实现 (Swift))
-
- [1. WKWebView 通信桥接](#1. WKWebView 通信桥接)
- [2. AppDelegate 路由控制](#2. AppDelegate 路由控制)
- 四、鸿蒙平台实现 (HarmonyOS)
-
- [1. WebView 通信桥接](#1. WebView 通信桥接)
- [2. Ability 控制器实现](#2. Ability 控制器实现)
- [3. 鸿蒙 WebView 配置](#3. 鸿蒙 WebView 配置)
- [五、通用 JavaScript 接口](#五、通用 JavaScript 接口)
-
- [1. 跨平台通信 SDK](#1. 跨平台通信 SDK)
- [2. HTML 页面集成](#2. HTML 页面集成)
- 六、平台差异处理表
- 七、高级功能实现
-
- [1. 双向实时通信](#1. 双向实时通信)
- [2. 文件传输支持](#2. 文件传输支持)
- [3. 安全控制措施](#3. 安全控制措施)
- 八、调试与监控
-
- [1. 统一日志系统](#1. 统一日志系统)
- [2. 性能监控](#2. 性能监控)
- 九、最佳实践建议
- 十、完整实现流程图
移动端多 WebView 互访方案:Android、iOS 与鸿蒙实现
一、核心架构设计
平台层 JS调用 消息路由 调用目标 执行JS Android Native Bridge iOS 鸿蒙 WebView A 平台控制器 WebView B WebView B页面
二、Android 平台实现
1. 基础通信架构
java
// 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);
}
}
}
2. 控制器实现
java
// 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);
}
}
}
3. WebView 安全配置
java
// 启用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);
}
三、iOS 平台实现 (Swift)
1. WKWebView 通信桥接
swift
// 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)
}
}
2. AppDelegate 路由控制
swift
// 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)
}
}
}
四、鸿蒙平台实现 (HarmonyOS)
1. WebView 通信桥接
java
// 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);
}
}
2. Ability 控制器实现
java
// 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);
}
}
}
3. 鸿蒙 WebView 配置
java
// 启用JavaScript
WebConfig webConfig = webView.getWebConfig();
webConfig.setJavaScriptPermit(true);
// 设置WebView代理
webView.setWebAgent(new WebAgent() {
@Override
public boolean onPageStart(WebView webView, String url) {
// 页面开始加载处理
return true;
}
});
五、通用 JavaScript 接口
1. 跨平台通信 SDK
javascript
// 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');
2. HTML 页面集成
html
<!-- webview_a.html -->
<!DOCTYPE html>
<html>
<head>
<title>WebView A</title>
<script src="bridge-sdk.js"></script>
</head>
<body>
<button onclick="sendMessage()">发送消息到WebView B</button>
<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 |
消息队列缓存 |
七、高级功能实现
1. 双向实时通信
javascript
// 在WebView A中
bridgeA.onMessage(function(message) {
console.log('实时消息:', message);
// 立即回复
bridgeA.postMessage('BridgeB', '收到消息');
});
// 在WebView B中
setInterval(() => {
bridgeB.postMessage('BridgeA', `心跳 ${Date.now()}`);
}, 5000);
2. 文件传输支持
java
// 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);
}
3. 安全控制措施
java
// 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;
}
八、调试与监控
1. 统一日志系统
javascript
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);
};
2. 性能监控
java
// 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秒监控一次
}
九、最佳实践建议
-
消息协议标准化
json{ "version": "1.0", "id": "uuid", "timestamp": 1685091200, "source": "BridgeA", "target": "BridgeB", "type": "text/json/file", "payload": {} }
-
错误处理机制
javascripttry { bridgeA.postMessage("BridgeB", largeData); } catch (e) { if (e.message.includes("Message too long")) { // 分片发送 sendInChunks(largeData); } }
-
心跳保活
java// 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; } }
十、完整实现流程图
WebView A Native Bridge Platform Router WebView B postMessage(target, data) 路由请求 调用目标bridge 执行onMessage (可选)回复消息 WebView A Native Bridge Platform Router WebView B
通过以上方案,可在Android、iOS和鸿蒙平台上实现高效稳定的WebView间通信。关键点在于:
- 使用标准化通信协议
- 统一JavaScript接口
- 平台特定桥接实现
- 完善的安全控制
- 性能优化措施
实际部署时建议:
- 在小流量环境验证
- 逐步完善错误处理
- 添加详细日志监控
- 定期进行安全审计