JSbridge通信机制介绍 (入门级别)

130 阅读16分钟

介绍背景

本文主要介绍Web(JS)和 Native(App)是如何“跨语言、跨环境”通信的


在移动端盛行的年代,纯原生开发和纯 Web 开发各有优劣:

1.原生语言(iOS/Android)性能高、功能覆盖全面,但是开发效率低,每次改动都要重新打包发布

2.Web H5开发跨平台,发布更新方便,但是性能较低,受限于浏览器或 WebView 的能力


所以出现了 混合开发(Hybrid)

  • 理念:用 JS/HTML/CSS 做业务逻辑和 UI,用原生提供底层能力
  • 目标兼顾开发效率、跨平台和性能

但是在混合开发(Hybrid App)中,页面(JS)运行在 WebView 里,而业务能力却掌握在原生 App 手中。

业务上需要

打开相册
获取定位
调起支付
登录态同步
  • 但是JS 无法直接调用原生 API ,原生也不能直接访问 JS 运行时

这就带来了一个核心问题:

WebView 和 Native 是两个完全不同的运行环境,它们如何通信?


JSBridge,就是为了解决 Web ↔ Native 通信问题而诞生的。


什么是JS bridge

JSBridge 是一套运行在 WebView 中的通信机制, 用于实现 JavaScript 与 Native 之间的双向调用


简单的说:JSBridge 是混合开发开发中JS 调用原生能力的“桥梁”

它在 APP 内方便地让 Native 调用 JSJS 调用 Native ,是双向通信的通道。

总结一句话:

  • WebView 是 JS 的沙箱环境,JSBridge 是 JS 与原生通信的桥梁

JSbridge主要包含什么?

从通信方向上看,JSBridge 并不复杂, 它本质上只解决了两件事:

  1. 1.JS 如何调用 Native 能力
  2. 2.Native 如何反向调用 JS

也就是我们常说的:

  • 1.JS Call Native
  • 2.Native Call JS

下面分别从这两个方向展开。


JS Call Native

Jsc Call Native是Web 页面向原生请求能力

目前市面上的主流实现方案有以下几种,我们分别介绍


  1. 1.URL Scheme 拦截
  2. 2.prompt / alert 拦截
  3. 3.WebView 注入对象(主流方案)
  4. 4.消息队列 + 回调机制 (工业级方案)

1.URL Scheme 拦截

原理WebView 可以加载 URL,而原生可以拦截 URL 的加载过程。

我们把通信伪装成一次页面跳转


在这个方案中:

  1. 1.JS 发起一个自定义协议的 URL 请求
  2. 2.WebView 将请求交给 Native 层处理
  3. 3.Native 判断该 URL 是否为约定协议
  4. 4.若是,则拦截并执行对应原生逻辑
  5. 5.阻止 WebView 继续加载该 URL

在这个过程中:

  • 页面并不会真正发生跳转
  • URL 只是一个通信信号

本质上看: URL Scheme 拦截并没有建立真正的通信通道

  • 它只是“借用”了浏览器的加载机制。

URL Scheme 的完整格式

scheme://method?query

1.scheme:协议名

2.method:方法名

3.query 参数是传给 Native 的数据


一个标准示例

myapp://openCamera?quality=80&source=album

  • 1.myapp → 自定义 Scheme(Native侧注册)

  • 2.openCamera → 要调用的原生方法

  • 3.quality=80&source=album → 参数


URL 在这里只是一个结构化的消息载体,不是为了跳转页面


JS 侧实现示例

//这里定义一个callNative函数 接收方法名 和参数
function callNative(method, params = {}) {
    //进行拼接
  const query = Object.keys(params)
    .map(key => `${key}=${encodeURIComponent(params[key])}`)
    .join('&')
    
    //发起URL请求
  location.href = `myapp://${method}?${query}`
}


//使用示例
callNative('openCamera', {
  quality: 80,
  source: 'album'
})

JS 不需要知道 Native 怎么实现 ,它只负责 发信号


Native 侧示例

这里以Android为例
(前端的同学不需要关心,这里是客户端该干的)

webView.setWebViewClient(new WebViewClient() {
    @Override
    public boolean shouldOverrideUrlLoading(WebView view, WebResourceRequest request) {
        Uri uri = request.getUrl();

        // 1. 判断是否是自定义 Scheme
        if ("myapp".equals(uri.getScheme())) {

            // 2. 解析方法名
            String method = uri.getHost(); // openCamera

            // 3. 解析参数
            String quality = uri.getQueryParameter("quality");
            String source = uri.getQueryParameter("source");

            // 4. 执行原生逻辑
            if ("openCamera".equals(method)) {
                openCamera(quality, source);
            }

            // 5. 拦截,不继续加载
            return true;
        }

        return false;
    }
});


URL Scheme缺点
1.URL Scheme是单向通信
  • 如果你注意我们之前写的JS代码
//使用示例
callNative('openCamera', {
  quality: 80,
  source: 'album'
})

你会发现,我们的函数没有接收result,也没有callback

因为这本质上是一次 页面加载行为

  • 1.JS 发起后立即结束
  • 2.没有返回值
  • 3.没有回调通道

JS 无法知道 Native 是否执行成功,也无法获取执行结果。


如果想要回传结果,常见的解决方案是Native 再反向执行一段 JS

(这里就不花时间介绍了,因为这个也不是现在的主流方案)


2.参数维护困难

URL 的设计初衷并不是用来传输复杂数据

如果这个方法传递的参数过长/ 涉及到数组和对象 等复杂数据类型

  • URL的表达非常混乱:

myapp://method?data=%7B%22a%22%3A1%2C%22b%22%3A...


3.无法支持复杂业务

就单拿异步回调任务来说,URL Scheme根本解决不了



2.prompt / alert 拦截

  • 其实这个也是一个过时的方案,当初它也只是作为过度方案

原理是:

  • JS中调用prompt / alert这些弹窗UI,其实它们不属于JS

或者说

  • 在 WebView 中,alert / confirm / prompt 这些弹窗 API 并不由 JavaScript 引擎自身实现, 而是 WebView 内核 转发给 Native 层来完成 UI 展示

链路是这样的:

JS EngineWebView 内核
   ↓
Native UI 系统

JS 调用 prompt,本质是在请求 Native 弹窗能力。


在 WebView 中:

  • 每一次 alert / prompt
  • 都会触发一个 Native 层回调
  • 并且 等待 Native 返回结果

这一步非常关键:

JS 线程会被阻塞,直到 Native 响应。

这就意味着:

  • 1.Native 可以在“中途”拦截
  • 2.不展示真实弹窗
  • 3.改为执行“通信逻辑”

而且prompt本身非常适合作为通信的载体

prompt 有返回值

1.prompt 有输入参数

prompt(message, defaultValue)

这两个参数可以被“借来”:

  • message → 通道标识 method
  • defaultValue → 通信数据 params

2.prompt 有返回值

const result = prompt(...)

完美解决了之前JS拿不到result的痛点


3.prompt 是同步的

JS 会 停在这一行

const result = prompt(...)

直到 Native 调用结束。

这使得:

JS → Native → JS 形成一次“同步调用链”。

(所以其实它还是解决不了异步的问题)


Native侧的拦截逻辑
JS 调用 prompt
  ↓
WebView 通知 Native:要弹窗
  ↓
Native 拦截该事件
  ↓
Native 解析参数
  ↓
Native 执行原生逻辑
  ↓
Native 返回字符串结果
  ↓
WebView 将结果交给 JS


JS侧实现示例
//封装函数 接收method 和 params
function callNative(method, params = {}) {
  const message = JSON.stringify({
    method,
    params
  })

  // prompt 的返回值由 Native 决定
  const result = prompt('JSBridge', message)

//深拷贝result
  try {
    return JSON.parse(result)
  } catch (e) {
    return result
  }
}



//使用演示

//接收同步结果
const res = callNative('openCamera', {
  quality: 80,
  source: 'album'
})

console.log(res)

Native 侧实现示例

Andorid (前端同学不用懂,你知道客户端处理了就行)

//设置 WebChromeClient

webView.setWebChromeClient(new WebChromeClient() {

    @Override
    public boolean onJsPrompt(
        WebView view,
        String url,
        String message,
        String defaultValue,
        JsPromptResult result
    ) {
        // 只拦截我们约定的协议
        if ("JSBridge".equals(defaultValue)) {
            handleJsBridge(message, result);
            return true; // 表示已处理,不弹窗
        }

        return super.onJsPrompt(view, url, message, defaultValue, result);
    }
});


//解析参数 执行逻辑 返回结果
private void handleJsBridge(String message, JsPromptResult result) {
    try {
        JSONObject json = new JSONObject(message);
        String method = json.getString("method");
        JSONObject params = json.getJSONObject("params");

        if ("openCamera".equals(method)) {
            // 执行原生逻辑
            openCamera(params);

            // 返回结果给 JS(同步)
            JSONObject response = new JSONObject();
            response.put("success", true);

            result.confirm(response.toString());
        } else {
            result.confirm("");
        }

    } catch (Exception e) {
        result.confirm("");
    }
}


prompt方案的缺点

1.prompt是同步API,会阻塞线程

JS使用prompt的时候

prompt('JSBridge', message)

  • 1.JS 线程会 立刻被阻塞

  • 2.必须等 Native 调用 result.confirm() 才能继续执行


这会导致

  • 1.页面渲染暂停

  • 2.动画卡顿

  • 3.复杂调用链极易产生“假死”体验

用户的体验非常差


2.无法支撑复杂通信模型

真实业务中,JSBridge 通常需要支持:

  • 1.异步回调
  • 2.并发调用
  • 3.Promise 化
  • 4.事件通知(Native → JS)
  • 5.消息队列

3.Webview注入对象

也叫API注入,是目前市面上最主流的解决方案,非常的强大

原理

Native 主动向 WebView 注入一个全局对象,供 JS 直接调用。

Native           WebView
  │                 │
  │ 注入对象         │
  └───────────────▶ window.NativeBridge
                      │
                      └─ JS 直接调用原生方法

这个对象身上包含了所有业务需要的功能函数


通信流程

  • 整体流程可以概括为:

1.Native 创建一个 Bridge 对象

2.将该对象注入到 WebView 的 JS 上下文

3.JS 通过 window.xxx 调用原生方法

4.Native 执行对应逻辑

5.JS通过回调/promise拿到结果


JS侧示例

//全局对象     方法
window.NativeBridge.openCamera({

  //参数
  quality: 80,
  source: 'album',
  
  //回调函数
  success(res) {
    console.log('success', res)
  },
  fail(err) {
    console.error(err)
  }
})


Native侧

//创建类
public class NativeBridge {

    @JavascriptInterface
    public void openCamera(String params) {
        // 解析参数
        // 执行原生逻辑
    }
}


//注入到JS
webView.getSettings().setJavaScriptEnabled(true);

webView.addJavascriptInterface(new NativeBridge(), "NativeBridge");



注意

由于 Android WebView 的限制:

  • 1.只支持基本类型
  • 2.通常使用 JSON 字符串 作为参数载体
window.NativeBridge.openCamera(JSON.stringify({
  quality: 80,
  source: 'album'
}))


优点和风险

优点

1.JS侧调用简单

window.NativeBridge.openCamera(params)

2.性能好,不阻塞JS线程

  • 不触发页面跳转

  • 不阻塞 JS 线程

  • 不涉及系统 UI


缺点

1.安全风险

Android 历史漏洞,在 Android 4.2 之前addJavascriptInterface

存在严重漏洞:

  • JS 可反射调用任意 Java 方法
  • 甚至执行系统命令
  • 属于高危远程代码执行漏洞

2.不利于异步与 Promise 化

JS 调用 → Native 执行 → JS 回调

1.回调需要 Native 主动拼接 JS

2.JS 需要提前注册全局函数

3.多个并发调用难以区分


4.消息队列 + 回调机制

当业务进入复杂阶段后,
简单的“方法注入”已经无法满足安全性、异步化和扩展性的要求


于是,现代 JSBridge 逐渐演进为一种更通用、更稳健的通信模型:

  • 基于消息队列的异步通信 + 回调机制

这也是目前主流 App(如支付宝、微信、小程序 WebView 等)所采用的设计思想。


原理

JS 与 Native 不再直接互相调用方法, 而是通过“消息”进行通信

JS                   Native
 │                      │
 │  发送消息             │
 │────────────────────▶│
 │                      │
 │        回调结果       │
 │◀────────────────────│

通信的最小单位是 Message(消息) ,而不是函数调用。


消息队列能够满足

  • 1.多次并发调用

  • 2.异步返回

  • 3.Promise 化

  • 4.Native 主动通知 JS

  • 5.错误隔离

  • 6.调用顺序保证


整体通信流程

1.JS 发起调用,生成唯一 callbackId

const callbackId = 'cb_' + Date.now() + '_' + Math.random()


2.JS 组装消息对象并入队

const message = {
  type: 'call',     
  method: 'openCamera',
  params: {
    quality: 80,
    source: 'album'
  },
  callbackId   //唯一id
}

JS 会将该消息:

  • 1.存入 发送队列
  • 2.记录对应的回调函数

3.JS 通知 Native「有新消息了」

常见方式:

  • 1.URL Scheme(只作为信号)
  • 2.postMessage
  • 3.evaluateJavascript

4.Native 拉取消息队列并处理

Native 主动:
  拉取 JS 消息队列
  ↓
  逐条解析
  ↓
  执行对应原生能力

5.Native 回传执行结果

Native 执行完成后:

  • 组装回调消息
  • 带上原始 callbackId
  • 发送给 JS

6.JS 根据 callbackId 执行回调

callbacks[callbackId](result)    //执行回调函数

delete callbacks[callbackId]     //从队列中删除


JS侧实现示例

//消息队列
const messageQueue = []

//回调函数
const callbacks = {}

//发起请求          方法  参数
function callNative(method, params) {

    //promise化
  return new Promise((resolve, reject) => {
  
      //生成唯一id
    const callbackId = 'cb_' + Date.now() + '_' + Math.random()
    
    //放入回调函数
    callbacks[callbackId] = resolve
    
    
    //推入消息队列
    messageQueue.push({
      method,
      params,
      callbackId
    })
    
    //通知原生
    notifyNative()
  })
}

//通知原生的函数
function notifyNative() {
  location.href = 'jsbridge://message'
}


//调用示例
window.handleNativeCallback = function (callbackId, result) {
  callbacks[callbackId]?.(result)
  delete callbacks[callbackId]
}

Native侧你无需关心


Native Call JS

本质上 Native Call JS 只有两类策略:

  1. 1.直接执行 JS(基于全局对象 / 函数)
  2. 2.基于消息机制的事件 / 回调分发(消息队列)

1.直接执行 JS(基于全局对象 / 函数)

原理

它和 JS Call Native 中的「WebView 注入全局 API」是同一套思想


JS Call Native 的全局注入方案

  • Native 在 WebView 初始化阶段
  • 向 JS 的 window注入一个全局对象或方法
  • JS 通过 window.xxx() 主动调用 Native 能力
// JS Call Native
window.NativeBridge.openCamera()


Native Call JS 的“直接执行 JS”方案:

  • JS 在页面加载时
  • 主动在 window 上暴露全局函数
  • Native 通过 WebView 的执行能力
  • 直接执行一段 JS 代码,调用这个全局函数
// Native Call JS
window.onNativeMessage(data)


JS 侧示例
// JS 侧:暴露给 Native 调用的全局函数
window.onNativeMessage = function (data) {
  console.log('收到来自 Native 的消息:', data)
}

// 可挂多个全局函数
window.onPayResult = function (result) {
  if (result.success) {
    console.log('支付成功,订单号:', result.orderId)
  } else {
    console.log('支付失败')
  }
}

注意:Native 只能调用挂在 window 上的函数,其他闭包或模块内部函数不可见。


Native 侧示例

Android(WebView)

// 构建要执行的 JS 字符串
String jsCode = "window.onNativeMessage(" +
        "{ type: 'login', userId: 123 }" +
        ");";


// 执行 JS
webView.evaluateJavascript(jsCode, null);

Native 并不直接调用 JS 函数,它只是把 JS 代码交给 WebView 执行


缺点

1.Native无法拿到JS的执行结果

Native直接执行 JS 的方式,本质上是“单向通知” ,不是双向通信

常见的解决方案是


方案 A:JS 再主动调用 Native 回调

window.onNativeMessage = function (data) {
  console.log('收到消息', data)
  
  // JS 执行完毕后,通知 Native
  if (window.NativeBridge && window.NativeBridge.callback) {
  
      //执行native提供的回调
    window.NativeBridge.callback(data)
  }
}

  • 1.Native 在 JS Call Native 时注入了一个回调对象

  • 2.JS 执行完毕后,再主动触发 Native 回调

  • 3.本质还是 两次跨环境调用


2.消息队列

原理:

1.Native 不直接找 JS 函数,只负责 发一条“消息” 给 JS

2.JS 端有一个统一的接收器(事件分发表 )去处理消息

3.消息里可以带一个 callbackId,JS 执行完后把结果通过同一通道发回 Native

4.这样就形成了一个“消息往返通道” ,Native 和 JS 都不需要知道对方内部实现


流程图
 Native
   │
   │  sendMessage({event:'loginSuccess', data:{userId:123}, callbackId:'cb_1'})
   ▼
┌───────────────────┐
│   WebView / JS     │
│   __handleNativeMessage │
└───────────────────┘
   │
   │ 分发事件到 JS 侧监听器
   ▼
JS 事件处理器 / 回调表
   │
   │ 执行 JS 逻辑
   │
   │ (执行完毕后)发送回调结果
   ▼
 Native 收到回调


JS 侧实现示例
// 全局事件分发 + 回调表
const eventListeners = {}
const callbackMap = {}

// 注册事件监听
function on(event, handler) {
  eventListeners[event] = eventListeners[event] || []
  eventListeners[event].push(handler)
}

// Native 消息入口
window.__handleNativeMessage = function(message) {

    //提取参数
  const { type, event, data, callbackId } = message
    
    //遍历事件分发表
  if (type === 'event') {
    (eventListeners[event] || []).forEach(fn => fn(data))
  }
    //执行回调函数
  if (type === 'callback' && callbackId) {
    callbackMap[callbackId]?.(data)
    delete callbackMap[callbackId]
  }
}



// JS 调用 Native 并提供回调(双向通信)
function callNativeWithCallback(method, params = {}, callback) {

    //唯一id
  const callbackId = 'cb_' + Date.now() + '_' + Math.random()
  
  //放入回调队列
  if (callback) callbackMap[callbackId] = callback
    
  const message = { method, params, callbackId }
  
  //传递信息
  window.NativeBridge?.postMessage(JSON.stringify(message))
}


Native 侧实现示例(Android)
// Native 向 JS 发送事件消息
public void sendEvent(WebView webView) {
    String message = "{"
        + "\"type\":\"event\","
        + "\"event\":\"loginSuccess\","
        + "\"data\":{\"userId\":123},"
        + "\"callbackId\":\"cb_1\""
        + "}";

    webView.evaluateJavascript("window.__handleNativeMessage(" + message + ")", null);
}

// Native 发送回调消息给 JS
public void sendCallback(WebView webView, String callbackId) {
    String message = "{"
        + "\"type\":\"callback\","
        + "\"callbackId\":\"" + callbackId + "\","
        + "\"data\":{\"success\":true}"
        + "}";

    webView.evaluateJavascript("window.__handleNativeMessage(" + message + ")", null);
}


使用示例
// 事件监听
on('loginSuccess', data => {
  console.log('收到事件', data)
})



// 调用 Native 并接收回调
callNativeWithCallback('pay', { orderId: 123 }, result => {
  console.log('支付结果', result)
})


从上面的示例,我们可以看到 在 JS 端,我们为 Native 消息提供了一个统一入口 window.__handleNativeMessage。它接收到消息后,会根据消息类型进行不同处理:


// Native 消息入口 
window.__handleNativeMessage = function(message) { 
//提取参数 const { type, event, data, callbackId } = message 

//1.事件event类型
if (type === 'event') { 
(eventListeners[event] || []).forEach(fn => fn(data)) 
} 


//2.回调callback
if (type === 'callback' && callbackId) { 
callbackMap[callbackId]?.(data) 

delete callbackMap[callbackId]

 }
}


1.事件event

message.type === 'event',Native侧主要发送过来的事件消息 (Native call JS)

JS 会在事件分发表 eventListeners 中查找对应的监听器,并依次调用它们,将数据传递过去。

if (type === 'event') { 
(eventListeners[event] || []).forEach(fn => fn(data)) 
} 


2.回调callBack

message.type === 'callback' 并且带有 callbackId,这是JS之前调用Native时,Native处理完成之后返回的结果。 (JS call Native)

JS 会根据 callbackIdcallbackMap 中找到对应的回调函数,执行它,并在执行完后删除(一次性回调)。


通过这个统一的消息入口,JS 既可以处理 Native 主动发送的事件,也能接收针对 JS 请求的回调结果,实现了双向通信。


完整的方案示例

演示完整的JSbridge双向通信封装


WebView全局注入API+消息队列+回调函数
// jsBridge.js - 企业级 WebView 封装
(function () {
  if (window.JSBridge) return;

  // ------------------- 内部队列 -------------------
  const eventListeners = {};   // 事件监听器,多播
  const callbackMap = {};      // 回调队列,一次性

  // ------------------- 注册事件 -------------------
  function on(event, handler) {
    eventListeners[event] = eventListeners[event] || [];
    eventListeners[event].push(handler);
  }

  // ------------------- JS 调用 Native -------------------
  function callNative(method, params = {}, callback) {
    const callbackId = 'cb_' + Date.now() + '_' + Math.random().toString(36).substr(2, 5);
    if (callback) callbackMap[callbackId] = callback;

    const message = { method, params, callbackId };

    try {
      // 调用 Native 全局注入的 postMessage
      window.NativeBridge?.postMessage(JSON.stringify(message));
    } catch (err) {
      console.error('[JSBridge] callNative error', err);
    }
  }

  // ------------------- Native 调用 JS 消息入口 -------------------
  function _handleMessage(message) {
    try {
      const { type, event, data, callbackId } = message;

      // 事件广播
      if (type === 'event' && event) {
        (eventListeners[event] || []).forEach(fn => fn(data));
      }

      // 回调响应
      if (type === 'callback' && callbackId && callbackMap[callbackId]) {
        callbackMap[callbackId](data);
        delete callbackMap[callbackId];
      }
    } catch (err) {
      console.error('[JSBridge] handleMessage error', err);
    }
  }

  // ------------------- 暴露给全局 -------------------
  window.JSBridge = {
    on,
    callNative,
    _handleMessage,
  };
})();


使用演示

1.JS 主动调用 Native 并拿回结果

JSBridge.callNative('getUserInfo', { id: 123 }, (data) => {
  console.log('Native 返回用户信息:', data);
});

流程如下:

  • 1.JSBridge.callNative → 调用封装好的方法

  • 'getUserInfo' → 你希望 Native 执行的功能名

  • { id: 123 } → 参数对象

  • (data) => { ... } → 回调函数,等 Native 执行完后返回结果时调用

  • 2.进入 callNative 方法

function callNative(method, params = {}, callback) {
  const callbackId = 'cb_' + Date.now() + '_' + Math.random().toString(36).substr(2, 5);
  
  这里把回调函数放入对应的队列  (key是callbackId)
  if (callback) callbackMap[callbackId] = callback;

  const message = { method, params, callbackId };
  ...
  
  //把消息发送给Native
  window.NativeBridge?.postMessage(JSON.stringify(message));
}

  • 3.Native 执行完方法后

  • Native 执行 getUserInfo 后,会通过 WebView 调用 JS:

window.JSBridge._handleMessage({
  type: 'callback',
  callbackId: 'cb_1678901234567_abcd1',
  data: { name: '张三', id: 123 }
});
  • type: 'callback' → 告诉 JS 这是回调消息

  • callbackId → 对应之前生成的唯一 ID

  • data → Native 执行结果

  • 4.JS 处理回调

  • 进入JS的 _handleMessage 方法

function _handleMessage(message) {
  const { type, event, data, callbackId } = message;
    
    在对应callbackMap里面通过callbackId这个key拿到要执行的回调函数
    
  if (type === 'callback' && callbackId && callbackMap[callbackId]) {
    callbackMap[callbackId](data);  // 执行回调
    delete callbackMap[callbackId]; // 删除,保证一次性
  }
}

  • 5.最终效果
JSBridge.callNative('getUserInfo', { id: 123 }, (data) => {
  console.log('Native 返回用户信息:', data);
});

整个流程走完后,控制台会输出:Native 返回用户信息: { name: '张三', id: 123 }

2.Native 主动通知 JS

(事件广播)

// 假设 Native 主动触发网络状态变化
JSBridge._handleMessage({
  type: 'event',
  event: 'networkChange',
  data: { status: 'online' }
});

// JS 注册监听
JSBridge.on('networkChange', (data) => {
  console.log('网络状态变化:', data.status);
});

使用示例

JSBridge.on('networkChange', (data) => {
  console.log('网络状态变化:', data.status);
});

  • 1.调用 JSBridge.on
function on(event, handler) {
  eventListeners[event] = eventListeners[event] || [];
  eventListeners[event].push(handler);
}
  • event = 'networkChange'

  • handler = (data) => { ... }

  • JSBridge 内部会变成

eventListeners = {
  networkChange: [
    function (data) {
      console.log('网络状态变化:', data.status);
    }
  ]
};

只是告诉 JSBridge:

“如果以后有人发 networkChange 事件,请执行这个函数。”

  • 2.Native广播事件

  • Native 调用封装好了的_handleMessag

window.JSBridge._handleMessage({
  type: 'event',
  event: 'networkChange',
  data: { status: 'online' }
});

  • 3.进入JS的统一入口_handleMessag
function _handleMessage(message) {
  const { type, event, data } = message;
    
  if (type === 'event' && event) {
    (eventListeners[event] || []).forEach(fn => fn(data));
  }
}

① 判断消息类型
type === 'event'

② 找到对应事件名
event === 'networkChange'

也就是eventListeners['networkChange']

③ 触发所有监听器
(eventListeners[event] || []).forEach(fn => fn(data));


在实际的开发中,我们还会移除监听事件

防止重复注册监听、内存泄漏


目前我们的实现的监听是

JSBridge.on('networkChange', handler)

其实是

eventListeners[event].push(handler)

我们再增加一个off监听的方法在封装的类里面

其实就是把这个回调函数在监听事件的回调数组里面删除
function off(event, handler) {
  const listeners = eventListeners[event];
  if (!listeners) return;

  const index = listeners.indexOf(handler);
  if (index > -1) {
    listeners.splice(index, 1);
  }

  // 可选:空数组直接清掉
  if (listeners.length === 0) {
    delete eventListeners[event];
  }
}

然后统一暴露出去

window.JSBridge = {
  on,
  off,
  callNative,
  _handleMessage,
};


至于callback为什么不用封装off

那是因为我们调用完成之后就直接delete了

callbackMap[callbackId](data)
delete callbackMap[callbackId]



总结

本文介绍了 JSBridge 的概念、原理及实现方式,包括 JS Call Native 和 Native Call JS 的常见策略,帮助读者理解在混合开发中,JS 与原生如何通过桥梁实现高效双向通信。