下面我将化身“移动端魔法建造师”,用一场跨岛奇遇故事 + 代码实战,带你彻底掌握JSBridge的设计精髓。准备好,我们要出发了!🚀
🌉 第一章:孤岛之谜——为什么需要JSBridge?
剧情:Native岛(Android世界)有强大的超能力(相机📷/GPS📍/文件读写),但造房子(UI开发)慢;Web岛(H5世界)造房子快⛵,却手无缚鸡之力。两岛隔海相望(运行环境隔离),急需合作却无法直接沟通。
技术本质:
WebView虽能加载H5,但JS与Java运行在独立沙箱中,就像两个语言不通的国家。JSBridge就是这座双向通信的魔法桥。
🛠️ 第二章:造桥三法——JS如何呼叫Native?
剧情:Web岛居民想调用Native岛的摄像头,工程师们提出三种建桥方案:
方案1:空投补给包(注入API)
Native岛空投一个名为NativeBridge的魔法包裹到Web岛:
java
// Android工程师建造"补给包"
webView.addJavascriptInterface(new Object() {
@JavascriptInterface // 安全封印!
public void openCamera(String type) {
if ("front".equals(type) || "back".equals(type)) {
startCamera(type); // 启动相机
}
}
}, "NativeBridge"); // 包裹名
javascript
// Web岛居民拆包即用
window.NativeBridge.openCamera("back");
✅ 优势:速度最快(直接调用)
⚠️ 风险:Android 4.2以下有安全漏洞,必须用@JavascriptInterface标记安全方法。
方案2:漂流瓶传信(拦截URL Scheme)
Web岛写需求塞入漂流瓶(特殊URL),投入大海🌊:
javascript
// 扔出漂流瓶(iframe保证连续发送不丢失)
const iframe = document.createElement('iframe');
iframe.src = "jsbridge://openCamera?type=back&callbackId=123";
document.body.appendChild(iframe);
java
// Native岛海关拦截
public boolean shouldOverrideUrlLoading(WebView view, String url) {
if (url.startsWith("jsbridge://")) {
parseCommand(url); // 解析指令
return true; // 拦截成功!
}
return false;
}
❌ 缺陷:瓶子可能丢失(URL长度限制)、速度慢。
方案3:信鸽快递(拦截prompt)
Web岛训练信鸽(prompt)携带加密指令:
javascript
// 派出信鸽(JSON避免长度限制)
const cmd = {method: "openCamera", args: "back"};
prompt(JSON.stringify(cmd));
java
// Native岛信鸽接收站
public boolean onJsPrompt(WebView view, String msg) {
if (msg.startsWith("{")) handleCommand(msg); // 解析指令
return true; // 阻止默认弹窗
}
💡 适用场景:Android 4.2以下的兼容方案8。
🔥 方案选择铁律:
📨 第三章:逆向通信——Native如何呼叫JS?
剧情:Native岛扫描完二维码,需用“无人机投递”将结果送回Web岛:
java
// Android派无人机(异步获取返回值)
webView.evaluateJavascript(
"javascript:onScanSuccess('QR_Code_Result')",
new ValueCallback<String>() {
@Override
public void onReceiveValue(String value) {
// 处理JS返回的数据
}
}
);
⚠️ 旧版兼容:
java
// Android 4.4以下用loadUrl(无返回值)
webView.loadUrl("javascript:onScanSuccess('QR_Code_Result')");
📜 第四章:通信协议设计——解决三大核心问题
问题1:如何避免消息混乱? → 协议统一
json
// 请求协议
{
"method": "pay", // 指令名
"callbackId": "K38x7", // 唯一回调ID
"data": {"amount": 100} // 参数
}
// 响应协议
{
"responseId": "K38x7", // 关联请求
"data": {"success": true} // 结果
}
问题2:如何管理回调? → 回调映射表
javascript
// Web岛的回调管理局
const callbacks = {};
let id = 0;
function callNative(method, data, callback) {
const callbackId = `cb_${id++}`;
callbacks[callbackId] = callback; // 存回调地址
send({ method, data, callbackId }); // 发送指令
}
// Native完成任务后回信
window.onNativeResult = (responseId, data) => {
callbacks[responseId](data); // 找到回调执行
delete callbacks[responseId]; // 清理内存
};
问题3:Web未加载完怎么办? → 消息队列
java
// Android端消息队列
List<Message> pendingMessages = new ArrayList<>();
void dispatchMessage(Message m) {
if (webViewLoaded) {
webView.evaluateJavascript(convertToJs(m));
} else {
pendingMessages.add(m); // 先存起来!
}
}
🛡️ 第五章:桥梁加固——安全防御指南
攻击剧情:海盗(恶意脚本)试图伪造指令!
防御工事:
- 白名单管控:仅允许
@JavascriptInterface标记的方法被调用1 - 参数核验:Native端严格检查参数类型和范围
java
@JavascriptInterface
public void transferMoney(String account, double amount) {
if (!isValidAccount(account) || amount <= 0) {
return; // 非法请求拒收!
}
// 执行转账
}
3. 权限最小化:关闭WebView不必要的存储/定位权限7
🚀 第六章:跨岛协同——实战支付流程
完整剧情:Web岛发起支付 → Native岛调起支付宝 → 结果回传6
javascript
// Web岛发起支付
callNative("alipay", {orderId: "2025xxx"}, (result) => {
if (result.success) showToast("支付成功!");
});
java
// Native岛处理支付
@JavascriptInterface
public void alipay(String json) {
PaymentData data = parseJson(json);
Alipay.execute(data, new PaymentCallback() {
@Override
public void onResult(boolean success) {
String js = String.format("window.onPayResult('%s')", success);
webView.evaluateJavascript(js); // 结果回传Web
}
});
}
💎 终章:桥梁的终极意义——为什么Hybrid永不落幕?
JSBridge不是一座冰冷的桥,而是效率与体验的平衡艺术:
- 开发速度:H5快速迭代页面 + 原生提供能力支持
- 动态更新:紧急Bug修复无需发版(替换H5资源)
- 体验升级:相比纯WebAPP,可调用硬件获得原生体验
🌈 未来之桥:小程序/React Native底层仍在复用JSBridge思想,掌握它等于握住了跨端开发的万能钥匙🔑
附录:造桥工程师速查手册 🧰
| 技术点 | 最佳方案 | 备选方案 | 核心原理 |
|---|---|---|---|
| JS调Native | 注入API (Android 4.2+) | 拦截prompt (兼容旧版) | 对象注入/消息拦截 |
| Native调JS | evaluateJavascript (4.4+) | loadUrl (兼容旧版) | JS代码执行 |
| 回调管理 | 唯一ID + 回调映射表 | JSONP模式 | 回调函数存储 |
| 消息队列 | 消息缓存+页面加载后分发 | 延时重发 | 异步任务调度 |