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
英文地址,請注明出處:http://en.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代碼沒…