想象一下:Web世界(H5页面)住着灵活的小精灵🧚♂️,Native世界(Android/iOS)住着强壮的巨人💪。他们需要合作,但被一道魔法屏障(运行环境隔离)隔开。JSBridge就是他们秘密通话的魔法对讲机!下面用故事+代码带你解密:
🎯 第一章:为什么需要JSBridge?
故事:Web小精灵想用巨人的摄像头(Native功能),但无法直接喊话。巨人想告诉小精灵有新消息(Native事件),但小精灵听不见。
解决方案:
建造一个双向通话系统!JSBridge = JavaScript + Bridge(桥梁)
🔧 第二章:通话原理(核心三板斧)
1️⃣ Web → Native:小精灵呼叫巨人
方式1:魔法漂流瓶(URL拦截)
javascript
Copy
// Web精灵:把消息塞进漂流瓶
function sendToNative(command) {
const iframe = document.createElement('iframe');
iframe.src = `jsbridge://camera/open?type=scan`; // 特殊协议
document.body.appendChild(iframe);
setTimeout(() => iframe.remove(), 0); // 立刻销毁瓶子
}
巨人侧(Android拦截):
java
Copy
webView.setWebViewClient(new WebViewClient() {
@Override
public boolean shouldOverrideUrlLoading(WebView view, String url) {
if (url.startsWith("jsbridge://")) {
openCamera(); // 执行Native操作
return true; // 拦截请求
}
return false;
}
});
方式2:魔法契约(API注入)
java
Copy
// Android巨人:给Web注入超能力
webView.addJavascriptInterface(new NativeBridge(), "NativeBridge");
public class NativeBridge {
@JavascriptInterface
public void openCamera(String type) {
// 调用摄像头
}
}
javascript
Copy
// Web精灵直接召唤能力!
window.NativeBridge.openCamera("scan");
2️⃣ Native → Web:巨人主动喊话
巨人侧(Android执行JS):
java
Copy
// 直接让Web精灵执行函数
String js = "window.receiveMessage('新消息来啦!')";
webView.evaluateJavascript(js, null); // 新API
// 或 webView.loadUrl("javascript:" + js); // 兼容旧版
Web精灵准备接收器:
javascript
Copy
window.receiveMessage = (message) => {
alert("巨人说:" + message);
};
3️⃣ 秘密暗号:回调魔法
故事:小精灵让巨人拍照后,需要知道结果。但巨人很忙,如何精准回复?
解决方案:每个请求带唯一暗号(callbackId)
javascript
Copy
// Web精灵:带暗号的请求
const callbackId = "req_" + Date.now();
const callbacks = {}; // 暗号本
callbacks[callbackId] = (result) => {
console.log("收到照片:", result);
};
sendToNative({
command: "takePhoto",
callbackId: callbackId
});
巨人完成时:
java
Copy
// 拍照完成后
String js = "window.handleNativeResponse('"
+ callbackId + "', 'base64:xxx')";
webView.evaluateJavascript(js, null);
Web统一接收站:
javascript
Copy
window.handleNativeResponse = (callbackId, data) => {
if (callbacks[callbackId]) {
callbacks[callbackId](data); // 找到对应回调
delete callbacks[callbackId]; // 销毁暗号
}
};
🌉 第三章:实战搭建JSBridge
完整Web端JSBridge实现:
javascript
Copy
class JSBridge {
constructor() {
this.callbacks = {};
window._handleNativeResponse = this._handleResponse.bind(this);
}
callNative(action, params) {
return new Promise((resolve, reject) => {
const callbackId = 'cb_' + Date.now();
this.callbacks[callbackId] = { resolve, reject };
// Android用注入,iOS用URL Scheme
if (window.NativeBridge) {
window.NativeBridge[action](JSON.stringify(params), callbackId);
} else {
const iframe = document.createElement('iframe');
iframe.src = `jsbridge://${action}?${JSON.stringify(params)}&cb=${callbackId}`;
document.body.appendChild(iframe);
setTimeout(() => iframe.remove(), 0);
}
});
}
_handleResponse(callbackId, result) {
if (this.callbacks[callbackId]) {
this.callbacks[callbackId].resolve(result);
delete this.callbacks[callbackId];
}
}
}
// 使用示例
const bridge = new JSBridge();
bridge.callNative('scanQRCode').then(data => {
console.log('扫码结果:', data);
});
Android端完整处理:
java
Copy
public class JSBridgeInterface {
private WebView webView;
public JSBridgeInterface(WebView webView) {
this.webView = webView;
}
@JavascriptInterface
public void scanQRCode(String callbackId) {
// 启动扫码Activity
Intent intent = new Intent(context, ScanActivity.class);
activity.startActivityForResult(intent, callbackId);
}
// 扫码结果回调
public void onScanResult(String result, String callbackId) {
String js = String.format(
"window._handleNativeResponse('%s', '%s')",
callbackId, result
);
webView.post(() -> webView.evaluateJavascript(js, null));
}
}
⚠️ 第四章:避坑指南
-
Android版本兼容:
- 4.2以下:用
@JavascriptInterface避免安全漏洞 - 4.4以下:用
loadUrl代替evaluateJavascript
- 4.2以下:用
-
iOS特殊处理:
swift Copy // WKWebView专属接收器 webView.configuration.userContentController.add( self, name: "nativeBridge" ) func userContentController(_ controller: WKUserContentController, didReceive message: WKScriptMessage) { if message.name == "nativeBridge" { handleMessage(message.body) // 处理消息 } } -
超时保护:
javascript Copy // Web侧增加超时控制 setTimeout(() => { if (callbacks[callbackId]) { callbacks[callbackId].reject("请求超时"); delete callbacks[callbackId]; } }, 5000);
🌟 第五章:开箱即用的方案
不想造轮子?直接用成熟方案:
-
DSBridge(Android/iOS双支持):
javascript Copy dsBridge.call("getLocation", {}, result => { console.log("位置:", result); }) -
WebViewJavascriptBridge(iOS专属):
javascript Copy bridge.callHandler('scanQRCode', {}, response => { console.log("扫码结果", response); })
🎓 终极总结
| 关键点 | 实现方式 | 类比 |
|---|---|---|
| Web→Native呼叫 | URL拦截/API注入 | 精灵扔漂流瓶🗣️ |
| Native→Web通知 | 执行JS字符串 | 巨人直接喊话📣 |
| 回调管理 | callbackId映射表 | 秘密暗号匹配🤫 |
| 双向通信完整流程 | 请求→存储→执行→回调→销毁 | 完整快递闭环📦 |
口诀:
一注(注入)、二拦(拦截)、三回调,
ID匹配不可少,
双向通信真奇妙!✨
现在你已经掌握了JSBridge的魔法!快去搭建你的Web-Native通信桥梁吧~ 🚀