WebView 載入 H5 頁面,JS 透過橋接層呼叫原生功能,原生執行完成後回呼 JS。需分別處理 Android 與 iOS 平台差異。
public class JsBridge {
@JavascriptInterface
public String getParams(String param) {
// 業務邏輯處理
return "Android 回傳: " + param;
}
@JavascriptInterface
public void nativeAction(String funcName, String jsonParam) {
// 異步處理,完成後呼叫 JS 回呼
new Handler(Looper.getMainLooper()).post(() -> {
String result = doBusiness(funcName, jsonParam);
webView.evaluateJavascript(
"javascript:window.JSBridgeCallback('" + funcName + "', '" + escape(result) + "')",
null
);
});
}
}
// WebView 設定
WebView webView = findViewById(R.id.webview);
webView.getSettings().setJavaScriptEnabled(true);
webView.addJavascriptInterface(new JsBridge(), "AndroidBridge");
window.JSBridge = {
callNative: function(funcName, param, callback) {
// 產生唯一回呼 ID
const callbackId = 'cb_' + Date.now() + '_' + Math.random().toString(36).substr(2, 9);
// 儲存回呼函式
window.JSBridge._callbacks = window.JSBridge._callbacks || {};
window.JSBridge._callbacks[callbackId] = callback;
// 呼叫原生
if (window.AndroidBridge) {
// Android: 同步回傳或異步回呼
try {
const result = window.AndroidBridge.nativeAction(funcName, JSON.stringify(param));
if (result && callback) callback(JSON.parse(result));
} catch (e) {
console.error('Android bridge error:', e);
}
} else if (window.webkit && window.webkit.messageHandlers) {
// iOS WKWebView
window.webkit.messageHandlers.iOSBridge.postMessage({
func: funcName,
param: param,
callbackId: callbackId
});
}
},
// 原生端呼叫此方法回傳結果
_handleCallback: function(callbackId, result) {
const callback = window.JSBridge._callbacks?.[callbackId];
if (callback) {
callback(result);
delete window.JSBridge._callbacks[callbackId];
}
}
};
// Swift
let config = WKWebViewConfiguration()
let userContentController = WKUserContentController()
userContentController.add(self, name: "iOSBridge")
config.userContentController = userContentController
let webView = WKWebView(frame: .zero, configuration: config)
webView.navigationDelegate = self
extension ViewController: WKScriptMessageHandler {
func userContentController(_ userContentController: WKUserContentController,
didReceive message: WKScriptMessage) {
guard message.name == "iOSBridge",
let body = message.body as? [String: Any],
let funcName = body["func"] as? String,
let callbackId = body["callbackId"] as? String else { return }
let param = body["param"]
// 異步處理業務
DispatchQueue.global().async {
let result = self.handleNativeFunction(funcName, param: param)
// 主執行緒回呼 JS
DispatchQueue.main.async {
let js = "window.JSBridge._handleCallback('\(callbackId)', \(result.toJSON()));"
self.webView.evaluateJavaScript(js, completionHandler: nil)
}
}
}
}
// JS 端統一介面自動判斷平台
JSBridge.callNative('getUserInfo', { userId: 123 }, (result) => {
console.log('用戶資訊:', result);
updateUI(result);
});
| 風險 | 防護措施 |
|---|---|
| 任意程式碼執行 | 僅暴露必要 API、驗證參數、禁止 eval() |
| XSS 攻擊 | CSP 標頭、輸入消毒、HTTPS 強制 |
| 敏感資料洩漏 | 最小權限原則、Token 過期、加密傳輸 |
| 記憶體洩漏 | 回呼清理、WeakReference、生命週期綁定 |
JS 呼叫 JSBridge.callNative()
↓
平台判斷
├── Android: window.AndroidBridge.nativeAction()
│ ↓
│ 原生處理 → webView.evaluateJavascript() → window.JSBridge._handleCallback()
│
└── iOS: window.webkit.messageHandlers.iOSBridge.postMessage()
↓
WKScriptMessageHandler → 原生處理 → webView.evaluateJavaScript() → window.JSBridge._handleCallback()
↓
JS 回呼函式執行,更新 UI