JSBridge原理与实现全解析

JSBridge 是用于连接 JavaScript(H5) 和原生应用(iOS/Android)的桥梁,允许它们之间相互调用方法。

🌉 一、JSBridge 双向通信流程图

Android
iOS
成功
失败
JavaScript 调用原生
平台判断
window.AndroidJSBridge.call
webkit.messageHandlers.postMessage
原生解析 method/params/callbackId
执行原生操作
设备信息/分享/相机等
invokeCallback 回调 JS
返回错误信息
JS 执行回调函数

⚙️二、基本调用模式

1. JavaScript桥接层

jsbridge-demo.js

// JS 调用原生方法
const callNativeMethod = (methodName, params, callback) => {// 生成唯一回调 IDconst callbackId = `cb_${Date.now()}_${Math.random().toString(36).substr(2, 5)}`;// 保存回调函数window.JSBridge.callbacks[callbackId] = callback;// 构造调用数据const data = {method: methodName,params: params,callbackId: callbackId};// 不同平台的调用方式if (/(iPhone|iPad|iPod|iOS)/i.test(navigator.userAgent)) {// iOS: 通过 WebView 的 URL Scheme 调用window.webkit.messageHandlers.JSBridge.postMessage(data);} else if (/(Android)/i.test(navigator.userAgent)) {// Android: 通过 addJavascriptInterface 提供的对象调用window.AndroidJSBridge.call(JSON.stringify(data));}
}// 原生调用 JS 回调的方法
window.JSBridge = {callbacks: {},// 原生通过此方法回调 JSinvokeCallback: (callbackId, result) => {const callback = window.JSBridge.callbacks[callbackId];if (callback && typeof callback === 'function') {callback(result);// 执行后删除回调,避免内存泄漏delete window.JSBridge.callbacks[callbackId];}}
};// 示例1:调用原生获取设备信息
callNativeMethod('getDeviceInfo', {}, function(result) {console.log('设备信息:', result);document.getElementById('deviceInfo').innerText = JSON.stringify(result);
});// 示例2:调用原生分享功能
document.getElementById('shareBtn').addEventListener('click', function() {callNativeMethod('share', {title: '分享标题',content: '分享内容',url: 'https://example.com'}, function(success) {if (success) {alert('分享成功');} else {alert('分享取消');}});
});
2. 原生端桥阶层实现

Android端(Java)
AndroidJSBridge.java

package com.example.jsbridge;import android.content.Context;
import android.os.Build;
import android.webkit.JavascriptInterface;
import android.webkit.WebView;
import org.json.JSONException;
import org.json.JSONObject;public class AndroidJSBridge {private final Context mContext;private final WebView mWebView;// 构造函数,接收上下文和WebView实例public AndroidJSBridge(Context context, WebView webView) {this.mContext = context;this.mWebView = webView;}// 暴露给JS调用的方法,必须加@JavascriptInterface注解@JavascriptInterfacepublic void call(String data) {try {// 解析JS传递的JSON数据JSONObject jsonData = new JSONObject(data);String method = jsonData.getString("method");JSONObject params = jsonData.getJSONObject("params");String callbackId = jsonData.getString("callbackId");// 根据方法名处理不同逻辑switch (method) {case "getDeviceInfo":handleGetDeviceInfo(callbackId);break;case "share":handleShare(params, callbackId);break;default:sendErrorCallback(callbackId, "Method not found: " + method);}} catch (JSONException e) {e.printStackTrace();}}// 处理获取设备信息private void handleGetDeviceInfo(String callbackId) {try {JSONObject result = new JSONObject();result.put("model", Build.MODEL);          // 设备型号result.put("brand", Build.BRAND);          // 设备品牌result.put("system", "Android " + Build.VERSION.RELEASE);  // 系统版本result.put("sdkInt", Build.VERSION.SDK_INT); // SDK版本sendSuccessCallback(callbackId, result);} catch (JSONException e) {e.printStackTrace();sendErrorCallback(callbackId, "Failed to get device info");}}// 处理分享功能private void handleShare(JSONObject params, String callbackId) {try {String title = params.getString("title");String content = params.getString("content");String url = params.getString("url");// 这里只是模拟分享成功,实际项目中应调用系统分享功能boolean shareSuccess = true;// 回调JS告知结果sendSuccessCallback(callbackId, new JSONObject().put("success", shareSuccess));} catch (JSONException e) {e.printStackTrace();sendErrorCallback(callbackId, "Share parameters error");}}// 发送成功回调private void sendSuccessCallback(String callbackId, JSONObject result) {String js = String.format("window.JSBridge.invokeCallback('%s', %s)",callbackId, result.toString());runOnMainThread(js);}// 发送错误回调private void sendErrorCallback(String callbackId, String errorMsg) {try {JSONObject error = new JSONObject();error.put("error", errorMsg);String js = String.format("window.JSBridge.invokeCallback('%s', %s)",callbackId, error.toString());runOnMainThread(js);} catch (JSONException e) {e.printStackTrace();}}// 在主线程执行JS代码private void runOnMainThread(final String js) {mWebView.post(() -> mWebView.evaluateJavascript(js, null));}
}

MainActivity.java

package com.example.jsbridge;import android.os.Bundle;
import android.webkit.WebSettings;
import android.webkit.WebView;
import androidx.appcompat.app.AppCompatActivity;public class MainActivity extends AppCompatActivity {private WebView mWebView;@Overrideprotected void onCreate(Bundle savedInstanceState) {super.onCreate(savedInstanceState);setContentView(R.layout.activity_main);mWebView = findViewById(R.id.webView);initWebView();}private void initWebView() {WebSettings webSettings = mWebView.getSettings();// 启用JavaScriptwebSettings.setJavaScriptEnabled(true);// 允许通过file协议加载的JS访问其他文件webSettings.setAllowFileAccessFromFileURLs(true);// 允许通过http协议加载的JS访问文件webSettings.setAllowUniversalAccessFromFileURLs(true);// 注册JSBridgemWebView.addJavascriptInterface(new AndroidJSBridge(this, mWebView), "AndroidJSBridge");// 加载本地HTML文件或远程URLmWebView.loadUrl("file:///android_asset/index.html");}@Overrideprotected void onDestroy() {super.onDestroy();// 销毁WebView防止内存泄漏if (mWebView != null) {mWebView.destroy();}}
}

iOS端(Swift)
IOSJSBridge.swift

import UIKit
import WebKitclass IOSJSBridge: NSObject, WKScriptMessageHandler {private weak var webView: WKWebView?init(webView: WKWebView) {self.webView = webViewsuper.init()}// 处理JS发送的消息func userContentController(_ userContentController: WKUserContentController, didReceive message: WKScriptMessage) {guard message.name == "JSBridge",let data = message.body as? [String: Any],let method = data["method"] as? String,let callbackId = data["callbackId"] as? String else {return}let params = data["params"] as? [String: Any] ?? [:]// 根据方法名处理不同逻辑switch method {case "getDeviceInfo":handleGetDeviceInfo(callbackId: callbackId)case "share":handleShare(params: params, callbackId: callbackId)default:sendErrorCallback(callbackId: callbackId, errorMsg: "Method not found: \(method)")}}// 处理获取设备信息private func handleGetDeviceInfo(callbackId: String) {let device = UIDevice.currentlet result: [String: Any] = ["model": device.model,"name": device.name,"system": "iOS \(device.systemVersion)","identifierForVendor": device.identifierForVendor?.uuidString ?? ""]sendSuccessCallback(callbackId: callbackId, result: result)}// 处理分享功能private func handleShare(params: [String: Any], callbackId: String) {guard let title = params["title"] as? String,let content = params["content"] as? String,let url = params["url"] as? String else {sendErrorCallback(callbackId: callbackId, errorMsg: "Invalid share parameters")return}// 这里只是模拟分享成功,实际项目中应调用系统分享功能let shareSuccess = truesendSuccessCallback(callbackId: callbackId, result: ["success": shareSuccess])}// 发送成功回调private func sendSuccessCallback(callbackId: String, result: [String: Any]) {guard let jsonData = try? JSONSerialization.data(withJSONObject: result),let jsonString = String(data: jsonData, encoding: .utf8),let webView = webView else {return}let js = "window.JSBridge.invokeCallback('\(callbackId)', \(jsonString))"webView.evaluateJavaScript(js, completionHandler: nil)}// 发送错误回调private func sendErrorCallback(callbackId: String, errorMsg: String) {let error = ["error": errorMsg]sendSuccessCallback(callbackId: callbackId, result: error)}
}

WebViewController.swift

import UIKit
import WebKitclass WebViewController: UIViewController {private var webView: WKWebView!override func viewDidLoad() {super.viewDidLoad()setupWebView()loadWebContent()}private func setupWebView() {// 配置WebViewlet config = WKWebViewConfiguration()let userContentController = WKUserContentController()// 创建JSBridge处理器并注册let iOSJSBridgeHandler = IOSJSBridge(webView: webView)userContentController.add(iOSJSBridgeHandler, name: "JSBridge")config.userContentController = userContentControllerwebView = WKWebView(frame: view.bounds, configuration: config)webView.navigationDelegate = selfwebView.autoresizingMask = [.flexibleWidth, .flexibleHeight]view.addSubview(webView)}private func loadWebContent() {// 加载本地HTML文件或远程URLif let url = Bundle.main.url(forResource: "index", withExtension: "html") {webView.loadFileURL(url, allowingReadAccessTo: url.deletingLastPathComponent())} else {// 备用:加载远程URLlet url = URL(string: "https://example.com")!webView.load(URLRequest(url: url))}}
}// 实现WKNavigationDelegate协议
extension WebViewController: WKNavigationDelegate {// 可以在这里处理页面加载事件
}

🛡️ 三、安全与性能优化建议

  1. 安全防护

    • 方法白名单校验:原生端只响应预注册的方法名
    if (!allowedMethods.contains(method)) { sendError(callbackId, "Method forbidden");
    }
    
    • 输入消毒:对JS传入参数进行正则校验
    • HTTPS 通信:防止中间人攻击
  2. 性能优化

    • 避免主线程阻塞:Android 使用 Handler 处理耗时操作
    • 回调超时机制:JS 端设置 5s 超时清理回调函数
    setTimeout(() => {delete JSBridge.callbacks[callbackId];
    }, 5000);
    
    • 大数据分片传输:超过 1MB 数据使用分块传输
  3. 跨平台兼容

    • 统一调用接口:封装 JSBridge.invoke() 抹平平台差异
    • 版本检测逻辑:
    const isIOS = /(iPhone|iPad)/i.test(navigator.userAgent);
    const isNewIOS = isIOS && !!window.webkit?.messageHandlers;
    

💻 四、完整调用示例

JavaScriptNativecallNative('share', {title:'Hello'}, cb)解析参数,执行分享invokeCallback(cbId, {success:true})执行回调函数JavaScriptNative

通过该设计,JSBridge 实现了 跨平台调用标准化(Android/iOS 接口统一)、双向通信可靠化(回调ID机制)、安全控制精细化(方法白名单+参数校验)。实际开发建议使用开源库(如https://github.com/marcuswestin/WebViewJavascriptBridge)减少底层适配成本。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。
如若转载,请注明出处:http://www.pswp.cn/diannao/93696.shtml
繁体地址,请注明出处:http://hk.pswp.cn/diannao/93696.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

Mockito:Java单元测试Mock框架

文章目录一、写在前面1、简介2、依赖二、使用1、基本使用2、注解(1)开启注解(2)Mock 注解(3)DoNotMock 注解(4)Spy 注解(5)Captor 注解(6&#xf…

群晖Synology Drive:打造高效安全的私有云协作平台

随着企业与个人对数据协作、安全与自主性的需求不断提升,群晖(Synology)推出的 Synology Drive 成为了私人云存储与团队协作的利器。下面将从功能亮点、使用方式、安全管理、适用场景等角度,为你全面解读这款强大的私有云方案。Sy…

开发避坑短篇(11):Oracle DATE(7)到MySQL时间类型精度冲突解决方案

异常信息 [Err] [Dtf] 1426 - Too-big precision 7 specified for CREATE_TIME. Maximum is 6.异常背景 用Navicat的数据传输功能进行oracle的数据表迁移到到mysql时报错。 异常分析 oracle的DATE类型的长度是7位,而mysql的datetime类型的长度最多6位,所…

怎么判断一个DAPP是否真正去中心化

判断一个DAPP(去中心化应用)是否真正去中心化,需要从多个维度进行考察。以下是关键评估标准:1. 区块链依赖程度✅ 真正去中心化:核心逻辑和数据处理完全依赖智能合约,运行在区块链上(如以太坊、…

F12 开发者工具 使用指北

F12 开发者工具 使用指北元素 Elements控制台 Console源代码 Sources网络 Network请求文件具体说明首先介绍Chrome开发者工具中,调试时使用最多的三个功能页面是:元素(ELements)、控制台(Console)、源代码&…

AD域设计与管理-域策略-进阶

AD域安全保密要求,也是最为常见的一些组策略配置需求 目录 1.禁止U盘,DVD,软盘等可移动存储使用 2.禁止员工自行安装软件 3.硬盘全部采用bitlocker上锁,密码保存至AD域控 4.密码复杂度要求 5.开启windows防火墙且不允许员工…

Python设计模式详解:策略模式(Strategy Pattern)实战指南

Python设计模式详解:策略模式实战指南什么是策略模式?核心组件基础实现利用Python特性的高级实现使用装饰器的策略模式策略模式的优势策略模式的适用场景实际应用案例:电商折扣系统注意事项总结在面向对象编程中,设计模式为常见问…

一次 web 请求响应中,通常那个部分最耗时?

文章目录一次Web请求的完整旅程1. DNS解析2. TCP连接建立3. 发送HTTP请求4. 服务器处理5. 服务器响应6. 浏览器渲染哪个环节通常最耗时?1. 数据库查询2. 外部API调用3. 复杂的业务逻辑如何优化各个环节?1. 数据库优化2. 缓存策略3. 异步处理总结一次Web请…

IO流-概述和体系

1.什么是I0流?存储和读取数据的解决方案|: input 0: output流:像水流一样传输数据2.10流的作用?用于读写数据(本地文件,网络)3. I0流按照流向可以分类哪两种流?输出流:程序-->文件输入流:文件-->程序4. I0流按照操作文件的类型可以分类哪两种流?…

提高建筑舒适度与能源效率,楼宇自控系统意义重大

随着城市化进程的加速和人们对建筑环境要求的不断提高,如何在保证建筑舒适度的同时提升能源效率,成为建筑行业面临的重要课题。楼宇自控系统(Building Automation System,简称BAS)作为现代智能建筑的核心组成部分&…

学习笔记《区块链技术与应用》第4天 比特币脚本语言

输入0.7 输出0.5 23个确认 不太可能回滚了交易id hash值 版本 locktime 交易剩下时间:0立即生效 confirmation:确认信息 time:产生时间 blocktime:块产生时间vout: 交易中第0个输入 scriptSig:输入脚本(input script)n…

3.Linux 系统文件类型与文件权限

1.文件类型Linux 下所有的东西都可以看做文件,Linux 将文件分为以下几种类型:普通文件 ‘-’目录文件 ‘d’管道文件 ‘p’链接文件 ‘l’设备文件(块设备 ’b’ 、字符设备 ‘c’)套接字文件 ‘s’Linux 上不以文件的扩展名区别文…

订单识别技术原理及场景应用

订单OCR(光学字符识别)技术通过图像处理和深度学习算法,将纸质或电子版订单中的文字信息转化为结构化数据。以下是其技术原理和典型应用场景的详细解析:一、技术原理剖析1. 核心处理流程图像预处理去噪:消除阴影、折痕…

[优选算法]复写零

题目链接 LeetCode复写零 题目描述 题目解析 一、问题理解 题目要求:给定一个整数数组 arr,在不创建新数组的情况下,将每个出现的 0 复写一遍(即一个 0 变成两个 0),同时保持其他元素的相对顺序不变。复…

element UI的el-table组件,实现可以拖动

表格 <div class"main_table"><el-table id"elTableid" :data"fieldArr" border style"width: 100%" row-class-name"drag-row"current-row-key highlight-current-row><el-table-column type"index&qu…

Android Emoji 全面解析:从使用到自定义

引言 Emoji已经成为现代数字通信不可或缺的一部分&#xff0c;这些小小的图标能够跨越语言障碍&#xff0c;直观地表达情感和想法。在Android开发中&#xff0c;正确处理和显示Emoji是提升用户体验的重要环节。本文将全面介绍Android平台上的Emoji支持&#xff0c;包括系统集成…

数据中心入门学习(五):服务器CPU

目录CPU1 概述1.1 概念1.2 冯诺依曼架构1.3 常见参数&#xff08;评估性能&#xff09;1.4 按指令集分类2 CPU发展2.1 发展史2.2 行业产业链2.3 英特尔 Xeon 至强处理器2.4 AMD Zen架构补充1 寄存器、存储器、内存、缓存、硬盘区别与联系&#xff1f;2 浮点单元参考本篇记录和梳…

基于MySQL实现基础图数据库

基于MySQL实现基础图数据库 一、概念 图数据库是一种用于存储和查询具有复杂关系的数据的数据库。在这种数据库中&#xff0c;数据被表示为节点&#xff08;实体&#xff09;和边&#xff08;关系&#xff09;。图数据库的核心优势在于能够快速地查询和处理节点之间的关系。 图…

RAG面试内容整理-9. 查询改写与增强(Query Rewriting, Query Expansion)

查询改写和查询增强是两种提升检索效果的技术,目标是在不改变用户意图的前提下,使检索器收到的查询更全面或明确,从而找到更多相关信息。 查询改写通常指将原始查询转换成语义等价但更明晰的形式。上一节谈到的对话查询改写是一个典型场景。在一般情况下,查询改写也适用于澄…

golang设置http代理

问题场景&#xff1a; golang通过eino的官方agent示例调用duckduckgo进行联网搜索时出现网络问题&#xff0c;电脑此时是挂了工具的浏览器整出打开 官方示例&#xff1a;https://www.cloudwego.io/zh/docs/eino/quick_start/agent_llm_with_tools/ 问题原因&#xff1a;go代码没…