核心内容摘要
从零开始写算法——贪心篇2:买卖股票的最佳时间 + 划分字母区间
移动开发必学JSBridge 的核心原理与最佳实践关键词JSBridge、Native-H5通信、移动混合开发、双向交互、WebView摘要在移动混合开发中H5页面与原生Native代码的通信是核心需求——小到调用相机拍照大到触发支付流程都需要两者“互相听懂对方的话”。
JSBridge 正是解决这一问题的“翻译官”。
本文将用“送快递”“打电话”等生活案例从原理到实战拆解 JSBridge 的核心机制并
总结开发中的避坑指南与最佳实践。
背景介绍目的和范围在“全民APP”时代混合开发Hybrid已成为主流APP 中大量页面用 H5 实现开发快、更新灵活但核心功能如支付、定位仍依赖原生能力。
JSBridge 就是连接 H5 与原生的“桥梁”本文将覆盖JSBridge 的核心通信原理双向交互主流实现方案URL 拦截、API 注入实战中的
常见问题与解决方法回调管理、安全校验跨平台最佳实践预期读者初级/中级移动开发工程师Android/iOS前端开发工程师需了解基础移动开发概念对混合开发感兴趣的技术爱好者文档结构概述本文从“为什么需要 JSBridge”出发用生活案例解释核心原理再通过代码实战演示具体实现最后
总结避坑指南与未来趋势。
术语表NativeAndroid/iOS 原生代码Java/Kotlin/Swift/Objective-CH5HTML5 页面运行在 WebView 中WebView移动设备中加载 H5 页面的容器类似手机里的“迷你浏览器”回调CallbackA 调用 B 后B 处理完结果再通知 A 的机制类似“留电话等回复”核心概念与联系故事引入小明的“跨语言”求助小明在手机上打开一个电商 APP 的 H5 商品页前端开发的页面想点击“分享到微信”。
但 H5 页面自己不会调微信的分享接口这是原生 APP 的能力就像小明只会说中文而微信分享功能的“负责人”原生代码只会说英文——两人无法直接沟通。
这时候就需要“翻译官”JSBridge把小明的中文请求H5 的 JS 代码翻译成英文原生能理解的指令再把英文的结果分享成功/失败翻译回中文H5 能理解的 JS 回调。
核心概念解释像给小学生讲故事核心概念一H5 调用 Native小明发请求H5 想让 Native 干活比如调相机需要通过 JSBridge 把需求“告诉”Native。
常见的两种方式URL 拦截H5 偷偷改一个特殊链接比如jsbridge://takePhoto?qualityhighNative 像“门卫”一样盯着 WebView 加载的所有 URL发现以jsbridge://开头的链接就知道是 H5 的请求然后解析参数调用相机。
API 注入Native 在 WebView 初始化时往 H5 的window对象里“塞”一个 JS 函数比如window.nativeBridge.takePhotoH5 直接调用这个函数就像打“直拨电话”Native 收到后立刻处理。
核心概念二Native 调用 H5原生回结果Native 干完活比如拍完照得到图片路径需要把结果告诉 H5。
这时候 Native 会通过 WebView 的“执行 JS 代码”功能直接运行 H5 提前准备好的回调函数比如window.onPhotoTaken(path/to/image)。
就像快递员送完快递后给发件人发条短信“已送达地址是 xxx”。
核心概念三回调管理避免“电话打错”H5 调用 Native 时可能同时发起多个请求比如同时调相机和定位。
为了区分每个请求的结果JSBridge 会给每个请求分配一个唯一 ID比如cb_123Native 处理完后带着这个 ID 回调 H5H5 就能知道哪个请求的结果到了。
就像外卖订单号——看到“订单 123”的取餐通知就知道是麻辣烫到了不是奶茶。
核心概念之间的关系用“快递流程”打比方H5 调用 Native 就像“下单”Native 处理后调用 H5 就像“送快递”而回调管理是“订单号”H5 调 Native下单用户H5通过电话URL 拦截/API 注入告诉快递员Native“我要寄快递调相机订单号是 123回调 ID”。
Native 调 H5送快递快递员Native送完快递后根据订单号回调 ID给用户H5发短信“快递已送达照片路径订单号 123”。
回调管理订单号用户H5根据订单号 123知道这是之前“调相机”的结果而不是“调定位”的结果。
核心原理的文本示意图H5 页面JS代码 ↔ JSBridge翻译官 ↔ Native 代码Android/iOS ↑ ↑ ↑ 调用相机 → JSBridge翻译 → Native调相机 → 结果 → JSBridge翻译 → H5回调Mermaid 流程图H5 调 Native 并回调渲染错误:Mermaid 渲染失败: Parse error on line 4: ....nativeBridge.action(param, cb_
] -----------------------^ Expecting SQE, DOUBLECIRCLEEND, PE, -), STADIUMEND, SUBROUTINEEND, PIPE, CYLINDEREND, DIAMOND_STOP, TAGEND, TRAPEND, INVTRAPEND, UNICODE_TEXT, TEXT, TAGSTART, got PS核心算法原理 具体操作步骤JSBridge 的核心是“双向通信协议”需要解决两个问题H5 如何通知 Native 执行操作请求发送Native 如何通知 H5 操作结果响应接收方案 1URL 拦截适合所有 WebView原理H5 通过修改window.location.href或a标签跳转一个特殊 URL如jsbridge://takePhoto?qualityhighcbcb_123Native 监听 WebView 的onPageFinished或shouldOverrideUrlLoading事件拦截所有 URL判断是否以jsbridge://开头。
如果是解析协议中的action操作类型、param参数、cb回调 ID执行对应操作后用cb回调 H5。
具体步骤以 Android 为例Native 监听 URL在 WebView 的WebViewClient中重写shouldOverrideUrlLoading方法。
webView.setWebViewClient(newWebViewClient(){OverridepublicbooleanshouldOverrideUrlLoading(WebViewview,WebResourceRequestrequest){Stringurlrequest.getUrl().toString();if(url.startsWith(jsbridge://)){parseJsBridgeUrl(url);// 解析 URLreturntrue;// 拦截 URL不跳转}returnsuper.shouldOverrideUrlLoading(view,request);}});解析 URL 参数提取action如takePhoto、param如{quality:high}、cb如cb_123。
执行 Native 操作根据action调用相机接口。
回调 H5操作完成后用webView.evaluateJavascript执行 JS 回调。
StringcallbackJsString.format(window.%s(%s),callbackId,result);webView.evaluateJavascript(callbackJs,null);方案 2API 注入适合支持addJavascriptInterface的平台原理Native 在 WebView 初始化时通过addJavascriptInterfaceAndroid或WKScriptMessageHandleriOS向 H5 的window对象注入一个 JS 对象如window.nativeBridgeH5 直接调用该对象的方法如nativeBridge.takePhoto(param, callback)Native 收到调用后执行操作再通过回调通知 H5。
具体步骤以 Android 为例Native 注入对象创建一个 Java 类用JavascriptInterface标记可被 JS 调用的方法。
publicclassJsBridge{JavascriptInterfacepublicvoidtakePhoto(Stringparam,StringcallbackId){// 解析 param如 {quality:high}// 调用相机Stringresult照片路径;// 回调 H5StringcallbackJsString.format(window.%s(%s),callbackId,result);webView.evaluateJavascript(callbackJs,null);}}// 注入到 WebViewwebView.addJavascriptInterface(newJsBridge(),nativeBridge);H5 调用方法直接调用window.nativeBridge.takePhoto。
constcallbackIdcb_Date.now();// 生成唯一回调 IDwindow.nativeBridge.takePhoto(JSON.stringify({quality:high}),callbackId);window[callbackId](result){console.log(照片路径,result);deletewindow[callbackId];// 清理回调};两种方案对比方案优点缺点URL 拦截兼容性好所有 WebView 都支持性能差每次调用需跳转 URL、参数长度受限URL 有长度限制API 注入性能高直接调用、参数支持复杂类型JSONAndroid 存在安全漏洞
2 以下可被攻击、iOS 需配合 WKWebView数学模型和公式 详细讲解 举例说明JSBridge 的通信可以抽象为一个“请求-响应”模型用数学公式表示为Request ( action , param , cbId ) \text{Request} (\text{action}, \text{param}, \text{cbId})Request(action,param,cbId)Response ( cbId , result ) \text{Response} (\text{cbId}, \text{result})Response(cbId,result)action \text{action}action操作类型如takePhoto、getLocationparam \text{param}param操作参数JSON 字符串cbId \text{cbId}cbId回调 ID唯一字符串如cb_1620000000000result \text{result}result操作结果JSON 字符串举例H5 调用 Native 拍照质量高流程如下H5 生成cbId cb_1620000000000构造请求KaTeX parse error: Expected EOF, got _ at position 78: …}}, \text{cb_̲1620000000000}…Native 收到请求后调用相机得到结果result file:///path/to/image.jpg构造响应KaTeX parse error: Expected EOF, got _ at position 30: …e} (\text{cb_̲1620000000000}…H5 收到响应后通过cbId找到对应的回调函数处理结果。
项目实战代码实际案例和详细解释说明开发环境搭建AndroidAndroid StudioAPI
WebViewiOSXcodeiOS
WKWebView前端VS Code、任意 H5 页面可本地用http-server启动源代码详细实现以 Android H5 为例步骤 1Android 端实现 JSBridge//
定义 JsBridge 类暴露给 JS 调用的方法classJsBridge(privatevalwebView:WebView){// JavascriptInterface 标记的方法可被 JS 调用JavascriptInterfacefuncallNative(action:String,param:String,callbackId:String){// 主线程执行WebView 操作需在主线程webView.post{when(action){takePhoto-handleTakePhoto(param,callbackId)getLocation-handleGetLocation(param,callbackId)else-{valerror未知操作:$actioninvokeJsCallback(callbackId,error)}}}}privatefunhandleTakePhoto(param:String,callbackId:String){// 模拟调用相机实际需申请权限、启动相机 Activityvalresultfile:///storage/emulated/0/DCIM/Camera/IMG_
jpginvokeJsCallback(callbackId,result)}privatefunhandleGetLocation(param:String,callbackId:String){// 模拟获取定位valresult{\lat\:
3
0, \lng\:
1
0}invokeJsCallback(callbackId,result)}// 执行 JS 回调privatefuninvokeJsCallback(callbackId:String,result:String){valjstry { window.$callbackId($result); } catch(e) { console.log(回调失败: e); }webView.evaluateJavascript(js,null)}}//
在 Activity 中初始化 WebView 并注入 JsBridgeclassMainActivity:AppCompatActivity(){overridefunonCreate(savedInstanceState:Bundle?){super.onCreate(savedInstanceState)valwebViewWebView(this).apply{settings.javaScriptEnabledtrue// 启用 JSaddJavascriptInterface(JsBridge(this),jsBridge)// 注入到 window.jsBridgeloadUrl(http://localhost:8080/index.html)// 加载 H5 页面}setContentView(webView)}}步骤 2H5 端调用 JSBridge!-- index.html --!DOCTYPEhtmlhtmlheadtitleJSBridge 示例/title/headbodybuttononclicktakePhoto()拍照/buttonbuttononclickgetLocation()定位/buttonscript// 生成唯一回调 IDfunctiongenerateCallbackId(){returncb_Date.now();}// 封装 JSBridge 调用方法functioncallJsBridge(action,param,callback){constcallbackIdgenerateCallbackId();window[callbackId](result){callback(result);deletewindow[callbackId];// 清理回调};// 调用 Native 注入的 jsBridge.callNative 方法window.jsBridge.callNative(action,JSON.stringify(param),callbackId);}// 拍照按钮点击事件functiontakePhoto(){callJsBridge(takePhoto,{quality:high},(result){alert(照片路径: result);});}// 定位按钮点击事件functiongetLocation(){callJsBridge(getLocation,{},(result){constlocationJSON.parse(result);alert(经纬度:${location.lat},${location.lng});});}/script/body/html代码解读与分析Android 端通过addJavascriptInterface注入jsBridge对象暴露callNative方法。
该方法接收action操作类型、param参数、callbackId回调 ID根据action执行对应逻辑完成后通过evaluateJavascript执行 H5 的回调函数。
H5 端封装callJsBridge函数生成唯一callbackId将回调函数暂存到window对象中调用window.jsBridge.callNative触发 Native 操作。
Native 回调时通过callbackId找到对应的函数并执行最后清理回调避免内存泄漏。
实际应用场景场景 1H5 调用原生功能案例电商 APP 的 H5 商品页点击“分享”调用原生的微信/朋友圈分享接口。
流程H5 调用jsBridge.callNative(share, { url: xxx, title: 商品 }, callbackId)Native 调微信 SDK 分享成功后回调 H5 显示“分享成功”。
场景 2原生通知 H5 更新案例金融 APP 的 H5 交易页原生收到支付结果通知如支付宝回调需要通知 H5 刷新页面。
流程Native 调用webView.evaluateJavascript(window.onPaymentResult(success))H5 监听onPaymentResult函数更新页面状态。
场景 3跨页面通信案例H5 页面跳转到原生登录页登录成功后原生通知 H5 刷新用户信息。
流程H5 跳转时传递callbackId原生登录成功后用该callbackId回调 H5H5 调用getUserInfo接口刷新数据。
工具和资源推荐工具/库描述适用平台WebViewJavascriptBridgeiOS 经典 JSBridge 库支持 WKWebViewiOSAndroidsBridgeAndroid 轻量级 JSBridge 封装Androiduni-app JSBridge跨平台App、小程序通信方案跨平台Chrome DevTools调试 WebView通过chrome://inspect连接Android/iOS未来发展趋势与挑战趋势 1更安全的通信协议传统addJavascriptInterface在 Android
2 以下存在 XSS 漏洞可执行任意 JS未来会更依赖WebMessagePortAndroid 11或WKScriptMessageHandleriOS 8等安全方案。
趋势 2跨端统一标准随着 Flutter、React Native 等跨端框架的普及JSBridge 可能向“跨端通信协议”演进如 Flutter 的MethodChannel已支持类似机制。
挑战 1性能优化H5 与 Native 的通信存在延迟尤其是 URL 拦截方案未来需要更高效的通信方式如内存共享、二进制协议。
挑战 2兼容性处理不同 WebView 版本如 iOS 的 UIWebView 与 WKWebView、不同系统版本Android
4 前后 WebView 内核变化的兼容性问题需要更健壮的适配层。
总结学到了什么核心概念回顾H5 调 Native通过 URL 拦截或 API 注入发送请求带action、param、callbackId。
Native 调 H5通过执行 JS 代码触发回调用callbackId找到对应函数。
回调管理用唯一callbackId区分不同请求的结果避免混乱。
概念关系回顾JSBridge 是 H5 与 Native 的“翻译官”通过“请求-响应”模型实现双向通信H5 发请求带回调 ID→ Native 处理 → Native 用回调 ID 通知 H5 结果。
思考题动动小脑筋为什么 H5 不能直接调用 Native 的方法提示WebView 的安全沙盒机制如果 H5 同时调用两次takePhotoJSBridge 如何保证回调不会混淆提示回调 ID 的唯一性如何防止恶意 H5 页面通过 JSBridge 调用敏感操作如支付提示白名单校验、来源域名校验附录
常见问题与解答QAndroid
2 以下用addJavascriptInterface为什么不安全A因为攻击者可以通过 XSS 注入 JS 代码调用被注入的 Native 方法如Runtime.exec执行系统命令。
Android
2 后增加了JavascriptInterface注解限制只有标记的方法可被 JS 调用提升了安全性。
QURL 拦截方案中参数太长怎么办A可以将参数用encodeURIComponent编码或通过localStorage传递H5 存参数到localStorageNative 读取。
QiOS 的 WKWebView 和 UIWebView 在 JSBridge 实现上有什么区别AUIWebView 主要用stringByEvaluatingJavaScriptFromString执行 JS而 WKWebView 用evaluateJavaScript异步更安全。
此外WKWebView 支持WKScriptMessageHandler监听 JS 消息替代传统的 URL 拦截。
扩展阅读 参考资料Android 官方文档WebView 与 JS 交互iOS 官方文档WKWebView 编程指南经典库源码WebViewJavascriptBridgeiOS安全指南OWASP WebView 安全顶 10