🚀 JSBridge框架:Web小精灵与Native巨人的通话密道

966 阅读3分钟

想象一下:Web世界(H5页面)住着灵活的小精灵🧚♂️,Native世界(Android/iOS)住着强壮的巨人💪。他们需要合作,但被一道魔法屏障(运行环境隔离)隔开。JSBridge就是他们秘密通话的魔法对讲机!下面用故事+代码带你解密:


🎯 第一章:为什么需要JSBridge?

​故事​​:Web小精灵想用巨人的摄像头(Native功能),但无法直接喊话。巨人想告诉小精灵有新消息(Native事件),但小精灵听不见。

​解决方案​​:

建造一个双向通话系统!JSBridge = JavaScript + Bridge(桥梁)

image.png


🔧 第二章:通话原理(核心三板斧)

1️⃣ ​​Web → Native:小精灵呼叫巨人​

​方式1:魔法漂流瓶(URL拦截)​

javascript
Copy
// Web精灵:把消息塞进漂流瓶
function sendToNative(command) {
  const iframe = document.createElement('iframe');
  iframe.src = `jsbridge://camera/open?type=scan`; // 特殊协议
  document.body.appendChild(iframe);
  setTimeout(() => iframe.remove(), 0); // 立刻销毁瓶子
}

​巨人侧(Android拦截)​​:

java
Copy
webView.setWebViewClient(new WebViewClient() {
  @Override
  public boolean shouldOverrideUrlLoading(WebView view, String url) {
    if (url.startsWith("jsbridge://")) {
      openCamera(); // 执行Native操作
      return true; // 拦截请求
    }
    return false;
  }
});

​方式2:魔法契约(API注入)​

java
Copy
// Android巨人:给Web注入超能力
webView.addJavascriptInterface(new NativeBridge(), "NativeBridge");

public class NativeBridge {
  @JavascriptInterface
  public void openCamera(String type) {
    // 调用摄像头
  }
}
javascript
Copy
// Web精灵直接召唤能力!
window.NativeBridge.openCamera("scan");

2️⃣ ​​Native → Web:巨人主动喊话​

​巨人侧(Android执行JS)​​:

java
Copy
// 直接让Web精灵执行函数
String js = "window.receiveMessage('新消息来啦!')";
webView.evaluateJavascript(js, null); // 新API
// 或 webView.loadUrl("javascript:" + js); // 兼容旧版

​Web精灵准备接收器​​:

javascript
Copy
window.receiveMessage = (message) => {
  alert("巨人说:" + message);
};

3️⃣ ​​秘密暗号:回调魔法​

​故事​​:小精灵让巨人拍照后,需要知道结果。但巨人很忙,如何精准回复?

​解决方案​​:每个请求带唯一暗号(callbackId)

javascript
Copy
// Web精灵:带暗号的请求
const callbackId = "req_" + Date.now();
const callbacks = {}; // 暗号本

callbacks[callbackId] = (result) => {
  console.log("收到照片:", result);
};

sendToNative({
  command: "takePhoto",
  callbackId: callbackId
});

​巨人完成时​​:

java
Copy
// 拍照完成后
String js = "window.handleNativeResponse('"
  + callbackId + "', 'base64:xxx')";
webView.evaluateJavascript(js, null);

​Web统一接收站​​:

javascript
Copy
window.handleNativeResponse = (callbackId, data) => {
  if (callbacks[callbackId]) {
    callbacks[callbackId](data); // 找到对应回调
    delete callbacks[callbackId]; // 销毁暗号
  }
};

🌉 第三章:实战搭建JSBridge

完整Web端JSBridge实现:

javascript
Copy
class JSBridge {
  constructor() {
    this.callbacks = {};
    window._handleNativeResponse = this._handleResponse.bind(this);
  }

  callNative(action, params) {
    return new Promise((resolve, reject) => {
      const callbackId = 'cb_' + Date.now();
      this.callbacks[callbackId] = { resolve, reject };

      // Android用注入,iOS用URL Scheme
      if (window.NativeBridge) {
        window.NativeBridge[action](JSON.stringify(params), callbackId);
      } else {
        const iframe = document.createElement('iframe');
        iframe.src = `jsbridge://${action}?${JSON.stringify(params)}&cb=${callbackId}`;
        document.body.appendChild(iframe);
        setTimeout(() => iframe.remove(), 0);
      }
    });
  }

  _handleResponse(callbackId, result) {
    if (this.callbacks[callbackId]) {
      this.callbacks[callbackId].resolve(result);
      delete this.callbacks[callbackId];
    }
  }
}

// 使用示例
const bridge = new JSBridge();
bridge.callNative('scanQRCode').then(data => {
  console.log('扫码结果:', data);
});

Android端完整处理:

java
Copy
public class JSBridgeInterface {
  private WebView webView;

  public JSBridgeInterface(WebView webView) {
    this.webView = webView;
  }

  @JavascriptInterface
  public void scanQRCode(String callbackId) {
    // 启动扫码Activity
    Intent intent = new Intent(context, ScanActivity.class);
    activity.startActivityForResult(intent, callbackId);
  }

  // 扫码结果回调
  public void onScanResult(String result, String callbackId) {
    String js = String.format(
      "window._handleNativeResponse('%s', '%s')",
      callbackId, result
    );
    webView.post(() -> webView.evaluateJavascript(js, null));
  }
}

⚠️ 第四章:避坑指南

  1. ​Android版本兼容​​:

    • 4.2以下:用@JavascriptInterface避免安全漏洞
    • 4.4以下:用loadUrl代替evaluateJavascript
  2. ​iOS特殊处理​​:

    swift
    Copy
    // WKWebView专属接收器
    webView.configuration.userContentController.add(
      self, name: "nativeBridge"
    )
    
    func userContentController(_ controller: WKUserContentController, 
                               didReceive message: WKScriptMessage) {
      if message.name == "nativeBridge" {
        handleMessage(message.body) // 处理消息
      }
    }
    
  3. ​超时保护​​:

    javascript
    Copy
    // Web侧增加超时控制
    setTimeout(() => {
      if (callbacks[callbackId]) {
        callbacks[callbackId].reject("请求超时");
        delete callbacks[callbackId];
      }
    }, 5000);
    

🌟 第五章:开箱即用的方案

不想造轮子?直接用成熟方案:

  1. ​DSBridge​​(Android/iOS双支持):

    javascript
    Copy
    dsBridge.call("getLocation", {}, result => {
      console.log("位置:", result);
    })
    
  2. ​WebViewJavascriptBridge​​(iOS专属):

    javascript
    Copy
    bridge.callHandler('scanQRCode', {}, response => {
      console.log("扫码结果", response);
    })
    

🎓 终极总结

​关键点​​实现方式​​类比​
Web→Native呼叫URL拦截/API注入精灵扔漂流瓶🗣️
Native→Web通知执行JS字符串巨人直接喊话📣
回调管理callbackId映射表秘密暗号匹配🤫
双向通信完整流程请求→存储→执行→回调→销毁完整快递闭环📦

​口诀​​:

一注(注入)、二拦(拦截)、三回调,
ID匹配不可少,
双向通信真奇妙!✨

现在你已经掌握了JSBridge的魔法!快去搭建你的Web-Native通信桥梁吧~ 🚀