【移动端知识】移动端多 WebView 互访方案:Android、iOS 与鸿蒙实现
移动端多 WebView 互访方案:Android、iOS 与鸿蒙实现
- 移动端多 WebView 互访方案:Android、iOS 与鸿蒙实现
- 一、核心架构设计
- 二、Android 平台实现
- 1. 基础通信架构
- 2. 控制器实现
- 3. WebView 安全配置
- 三、iOS 平台实现 (Swift)
- 1. WKWebView 通信桥接
- 2. AppDelegate 路由控制
- 四、鸿蒙平台实现 (HarmonyOS)
- 1. WebView 通信桥接
- 2. Ability 控制器实现
- 3. 鸿蒙 WebView 配置
- 五、通用 JavaScript 接口
- 1. 跨平台通信 SDK
- 2. HTML 页面集成
- 六、平台差异处理表
- 七、高级功能实现
- 1. 双向实时通信
- 2. 文件传输支持
- 3. 安全控制措施
- 八、调试与监控
- 1. 统一日志系统
- 2. 性能监控
- 九、最佳实践建议
- 十、完整实现流程图
移动端多 WebView 互访方案:Android、iOS 与鸿蒙实现
一、核心架构设计
二、Android 平台实现
1. 基础通信架构
// 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);}@JavascriptInterfacepublic 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. 控制器实现
// MainActivity.java
public class MainActivity extends AppCompatActivity {private WebView webViewA, webViewB;private WebViewBridge bridgeA, bridgeB;@Overrideprotected 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");// 加载HTMLwebViewA.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 安全配置
// 启用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 通信桥接
// WebViewBridge.swift
class WebViewBridge: NSObject, WKScriptMessageHandler {private weak var webView: WKWebView?private let bridgeName: Stringinit(webView: WKWebView, bridgeName: String) {self.webView = webViewself.bridgeName = bridgeNamesuper.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 路由控制
// 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 {// 创建WebViewwebViewA = WKWebView(frame: .zero)webViewB = WKWebView(frame: .zero)// 初始化桥接bridgeA = WebViewBridge(webView: webViewA, bridgeName: "BridgeA")bridgeB = WebViewBridge(webView: webViewB, bridgeName: "BridgeB")// 加载HTMLif 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 通信桥接
// 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);}@JSFunctionpublic void postMessage(String targetBridge, String message) {// 获取主AbilityMainAbility mainAbility = (MainAbility) context;mainAbility.routeMessage(targetBridge, message);}public void receiveMessage(String message) {String js = "window." + bridgeName + ".onMessage('" + message + "')";webView.executeJs(js);}
}
2. Ability 控制器实现
// MainAbility.java
public class MainAbility extends Ability {private WebView webViewA;private WebView webViewB;private HarmonyBridge bridgeA;private HarmonyBridge bridgeB;@Overridepublic void onStart(Intent intent) {super.onStart(intent);DirectionalLayout layout = new DirectionalLayout(this);// 创建WebViewAwebViewA = 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");// 创建WebViewBwebViewB = 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 配置
// 启用JavaScript
WebConfig webConfig = webView.getWebConfig();
webConfig.setJavaScriptPermit(true);// 设置WebView代理
webView.setWebAgent(new WebAgent() {@Overridepublic boolean onPageStart(WebView webView, String url) {// 页面开始加载处理return true;}
});
五、通用 JavaScript 接口
1. 跨平台通信 SDK
// bridge-sdk.js
class CrossWebViewBridge {constructor(bridgeName) {this.bridgeName = bridgeName;this.messageHandlers = {};}postMessage(targetBridge, message) {// Androidif (window.AndroidBridge) {window.AndroidBridge.postMessage(targetBridge, message);}// iOSelse if (window.webkit && window.webkit.messageHandlers[this.bridgeName]) {window.webkit.messageHandlers[this.bridgeName].postMessage({target: targetBridge,message: message});}// HarmonyOSelse 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 页面集成
<!-- 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. 双向实时通信
// 在WebView A中
bridgeA.onMessage(function(message) {console.log('实时消息:', message);// 立即回复bridgeA.postMessage('BridgeB', '收到消息');
});// 在WebView B中
setInterval(() => {bridgeB.postMessage('BridgeA', `心跳 ${Date.now()}`);
}, 5000);
2. 文件传输支持
// 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);}// 通知目标WebViewString message = "file://" + file.getAbsolutePath();routeMessage("BridgeB", message);
}
3. 安全控制措施
// 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. 统一日志系统
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. 性能监控
// Android 性能监控
private void startPerformanceMonitoring() {new Timer().scheduleAtFixedRate(new TimerTask() {@Overridepublic 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 {@Overridepublic 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间通信。关键点在于:
- 使用标准化通信协议
- 统一JavaScript接口
- 平台特定桥接实现
- 完善的安全控制
- 性能优化措施
实际部署时建议:
- 在小流量环境验证
- 逐步完善错误处理
- 添加详细日志监控
- 定期进行安全审计