JSBridge 原理详解
什么是 JSBridge
JSBridge 是 WebView 中 JavaScript 与 Native 代码之间的通信桥梁。核心问题是:两个不同运行环境的代码如何互相调用?
通信原理
1. Native 调用 JS(简单)
WebView 本身就提供了执行 JS 的能力,原理很直接:WebView 控制着 JS 引擎,可以直接向其注入并执行代码。
java
// Android
webView.evaluateJavascript("window.appCallJS('data')", null);
// iOS
webView.evaluateJavaScript("window.appCallJS('data')")
// Flutter
webViewController.runJavaScript("window.appCallJS('data')");2. JS 调用 Native(核心难点)
JS 运行在沙箱中,无法直接访问系统 API。有两种主流方案:
方案一:注入 API
Native 在 WebView 初始化时,向 JS 全局对象注入方法:
java
// Android - 注入对象到 window
webView.addJavascriptInterface(new Object() {
@JavascriptInterface
public void showToast(String msg) {
Toast.makeText(context, msg, Toast.LENGTH_SHORT).show();
}
}, "NativeBridge");JS 端直接调用:
javascript
window.NativeBridge.showToast("Hello")本质:Native 把自己的方法"挂"到了 JS 的全局作用域里。
方案二:URL Scheme 拦截
JS 发起一个特殊协议的请求,Native 拦截并解析:
javascript
// JS 端
location.href = 'jsbridge://showToast?msg=Hello'
// 或使用 iframe(避免页面跳转)
const iframe = document.createElement('iframe')
iframe.src = 'jsbridge://showToast?msg=Hello'
document.body.appendChild(iframe)java
// Android 端拦截
webView.setWebViewClient(new WebViewClient() {
@Override
public boolean shouldOverrideUrlLoading(WebView view, String url) {
if (url.startsWith("jsbridge://")) {
// 解析 url,执行对应 Native 方法
return true;
}
return false;
}
});本质:利用 WebView 的 URL 加载机制作为通信通道。
异步回调的实现
JS 调用 Native 后如何拿到返回值?通过回调 ID 机制:
javascript
// JS 端
let callbackId = 0
const callbacks = {}
function callNative(method, params) {
return new Promise((resolve) => {
const id = callbackId++
callbacks[id] = resolve
// 告诉 Native:调用完成后,用这个 id 回调我
window.NativeBridge.invoke(JSON.stringify({
method,
params,
callbackId: id
}))
})
}
// Native 执行完后调用这个函数
window.handleCallback = (id, result) => {
callbacks[id]?.(result)
delete callbacks[id]
}流程:JS 调用 → Native 处理 → Native 调用 evaluateJavascript 执行回调函数 → JS 收到结果
各平台注入对象命名
| 平台/插件 | 全局对象名 | 是否可自定义 |
|---|---|---|
| Android 原生 | 任意 | ✅ 完全自定义 |
| iOS WKWebView | webkit.messageHandlers.xxx | ✅ xxx 部分可自定义 |
| flutter_inappwebview | flutter_inappwebview | ❌ 插件固定 |
| webview_flutter | 需要自己实现 | ✅ 完全自定义 |
Android 示例
java
// 第二个参数就是 JS 中的对象名,可以随便取
webView.addJavascriptInterface(bridgeObject, "MyBridge");javascript
// JS 端
window.MyBridge.method()iOS 示例
swift
// name 就是 JS 中的 handler 名
configuration.userContentController.add(self, name: "iOSBridge")javascript
// JS 端
window.webkit.messageHandlers.iOSBridge.postMessage(data)Flutter (flutter_inappwebview) 示例
dart
// Flutter 端注册 handler,handlerName 可自定义
webViewController.addJavaScriptHandler(
handlerName: 'myCustomHandler',
callback: (args) { ... }
);javascript
// JS 端,flutter_inappwebview 是固定的
window.flutter_inappwebview.callHandler('myCustomHandler', data)通信方式总结
| 方向 | 原理 | 实现方式 |
|---|---|---|
| Native → JS | WebView 控制 JS 引擎 | evaluateJavascript |
| JS → Native | 注入或拦截 | addJavascriptInterface / URL Scheme |
常见通信方式对比
| 方式 | 优点 | 缺点 | 适用场景 |
|---|---|---|---|
| JavaScript Bridge | 双向通信、支持回调 | 需要约定协议 | 复杂交互 |
| URL Scheme | 简单、兼容性好 | 单向、数据量有限 | 简单跳转 |
| postMessage | 标准 API | 需要 WebView 支持 | iframe 通信 |
| 注入 JS 对象 | 调用方便 | Android 4.2 以下有安全漏洞 | 频繁调用 |
最佳实践建议
- 统一封装:抽离成独立的 bridge 工具类,统一管理通信逻辑
- 消息队列:处理 Native 未就绪时的调用,避免丢失消息
- 超时处理:添加超时机制,防止回调永远不返回
- 类型安全:使用 TypeScript 定义消息类型
- 错误处理:统一的错误捕获和上报机制