JSBridge原理与实现:从H5到Native的双向通信

作者:阿沐时间:2026-10-24 10:15:59浏览:569

本文深入解析JSBridge在Hybrid开发中的核心作用,涵盖H5与Native双向通信原理。详细对比拦截URL Scheme、重写prompt及注入API三种JS调用Native的实现方式,并阐述Native调用JS的loadUrl与evaluateJavascript差异。结合Android与iOS具体代码示例,探讨H5本地引用与Native注入两种引用规范及兼容性处理,为移动端混合开发提供完整技术参考。

JSBridge 的起源

随着移动互联网的普及,在移动端开发中,选择原生(Native)还是网页(H5)一直是技术选型的热点。两者各有优劣,为了适应复杂业务需求,实际项目中常采用混合开发(Hybrid)模式,结合两者的优势。既然 Native 应用与 H5 页面运行环境不同,如何建立有效的通信机制以协同工作,成为关键问题。

早期,Cordova 提供了一组设备相关的 API,成为 JavaScript 调用原生功能的主流方案之一。然而,JSBridge 在国内的真正普及与广泛应用,得益于移动互联网的高速发展及混合开发模式的成熟。

JSBridge 本质上是连接 Native 应用与 H5 页面的桥梁,基于 JavaScript 实现。它构建了双向通信通道,允许 Native 调用 JS 代码,同时也支持 JS 调用 Native 代码。其主要价值在于赋予 H5 页面调用原生能力,如访问本地相册、调用摄像头或进行指纹支付等功能。

H5 与 Native 对比

name H5 Native
稳定性 调用系统浏览器内核,稳定性较差 使用原生内核,更加稳定
灵活性 版本迭代快,上线灵活 迭代慢,需要应用商店审核,上线速度受限制
受网速 影响 较大 较小
流畅度 有时加载慢,给用户“卡顿”的感觉 加载速度快,更加流畅
用户体验 功能受浏览器限制,体验有时较差 原生系统 api 丰富,能实现的功能较多,体验较好
可移植性 兼容跨平台跨系统,如 PC 与 移动端,iOS 与 Android 可移植性较低,对于 iOS 和 Android 需要维护两套代码

JSBridge 的双向通信原理

JS 调用 Native

实现 JS 调用 Native 的方式多样,主要包括拦截 URL Scheme、重写原生 JS 方法(如 prompt)以及注入 API 对象等几种常见手段。

拦截 URL Scheme

Android 和 iOS 平台均支持通过拦截 URL Scheme 并解析其内容,由 Native 端决定是否执行相应的处理逻辑。

在 Android 平台中,WebView 提供了 shouldOverrideUrlLoading 方法,供 Native 端拦截 H5 发出的 URL Scheme 请求。具体实现代码如下:


public class CustomWebViewClient extends WebViewClient {
    @Override
    public boolean shouldOverrideUrlLoading(WebView view, String url) {
      ......
      // 场景一: 拦截请求、接收 scheme
        if (url.equals("xxx")) {

            // handle
            ...
            // callback
            view.loadUrl("JavaScript:setAllContent(" + json + ");")
            return true;
        }
        return super.shouldOverrideUrlLoading(url);
     }
}

在 iOS 平台中,WKWebView 可根据拦截到的 URL Scheme 及其参数执行对应操作。相关代码如下:


- (void)webView:(WKWebView *)webView decidePolicyForNavigationAction:(WKNavigationAction *)navigationAction decisionHandler:(void (^)(WKNavigationActionPolicy))decisionHandler{
    if ([navigationAction.request.URL.absoluteString hasPrefix:@"xxx"]) {
        [[UIApplication sharedApplication] openURL:navigationAction.request.URL];
    }
    decisionHandler(WKNavigationActionPolicyAllow);
}

该方法的优点在于不存在安全漏洞,使用灵活,并能实现 H5 与 Native 页面的无缝切换。例如,在紧急上线场景下,可先发布 H5 页面;待 Native 页面开发完成后,通过拦截逻辑跳转至 Native 页面,而无需修改 H5 链接。不过,使用 iframe.src 发送 URL Scheme 时需控制 URL 长度,且实现较为复杂,执行速度相对较慢。

重写 prompt 等原生 JS 方法

在 Android 4.2 之前,注入对象的接口为 addJavaScriptInterface,但因安全原因逐渐被淘汰。目前通常通过修改浏览器部分 Window 对象的方法来实现交互,主要拦截 alert、confirm、prompt 和 console.log 四个方法,分别由 WebView 的 onJsAlert、onJsConfirm、onConsoleMessage 和 onJsPrompt 监听。其中,onJsPrompt 的监听代码如下:


public boolean onJsPrompt(WebView view, String origin, String message, String defaultValue, final JsPromptResult result) {
  String handledRet = parentEngine.bridge.promptOnJsPrompt(origin, message,defaultValue);
  xxx;
  return true;
}

iOS 出于安全机制考虑,WKWebView 对 alert、confirm、prompt 等方法进行了拦截。若通过此方式进行 Native 与 JS 交互,需实现 WKWebView 的三个 WKUIDelegate 代理方法。代码示例如下:


-(void)webView:(WKWebView *)webView runJavaScriptAlertPanelWithMessage:(NSString *)message initiatedByFrame:(WKFrameInfo *)frame completionHandler:(void (^)(void))completionHandler{

  UIAlertController *alertController = [UIAlertController                    alertControllerWithTitle:nil message:message?:@"" preferredStyle:UIAlertControllerStyleAlert];

  [alertController addAction:([UIAlertAction actionWithTitle:@"确认" style:UIAlertActionStyleDefault handler:^(UIAlertAction * _Nonnull action) {

      completionHandler();

  }])];

  [self presentViewController:alertController animated:YES completion:nil];

}

使用此方式时,H5 与 Native 可约定传参格式。H5 无需识别客户端类型,只需传入不同参数即可调用 Native 功能;Native 端则通过拦截相同方法并识别参数,执行各自的处理逻辑,从而实现多端表现一致。示例如下:


alert("确定xxx?", "取消", "确定", callback());

此外,若与 Native 确定了方法名和传参等调用协议规范,其他格式的 prompt 等方法将不会被识别,从而起到隔离作用,防止意外调用。

##### 注入 API

基于 WebView 提供的能力,可以向 Window 对象注入对象或方法。JS 通过该对象或方法调用时,执行对应的 Native 逻辑,实现直接调用 Native 方法。使用此方式时,JS 需等待 Native 执行完相应逻辑后,才能进行回调操作。

Android 的 WebView 提供了 addJavascriptInterface 方法,支持 Android 4.2 及以上系统。


gpcWebView.addJavascriptInterface(new JavaScriptInterface(), 'nativeApiBridge'); 
public class JavaScriptInterface {
    Context mContext;

  JavaScriptInterface(Context c) {
    mContext = c;
  }

  public void share(String webMessage){            
    // Native 逻辑
  }
}

JS 调用示例:


window.NativeApi.share(xxx);

iOS 的 UIWebView 提供了 JavaScriptScore 方法,支持 iOS 7.0 及以上系统。WKWebView 提供了 window.webkit.messageHandlers 方法,支持 iOS 8.0 及以上系统。UIWebView 已较少使用,以下为创建 WKWebViewConfiguration 和 WKWebView 的示例:


WKWebViewConfiguration *configuration = [[WKWebViewConfiguration alloc] init];
WKPreferences *preferences = [WKPreferences new];
preferences.javaScriptCanOpenWindowsAutomatically = YES;
preferences.minimumFontSize = 40.0;
configuration.preferences = preferences;
    

- (void)viewWillAppear:(BOOL)animated
{
    [super viewWillAppear:animated];
    [self.webView.configuration.userContentController addScriptMessageHandler:self name:@"share"];
      [self.webView.configuration.userContentController addScriptMessageHandler:self name:@"pickImage"];
}
- (void)viewWillDisappear:(BOOL)animated
{
    [super viewWillDisappear:animated];
    [self.webView.configuration.userContentController     removeScriptMessageHandlerForName:@"share"];
    [self.webView.configuration.userContentController removeScriptMessageHandlerForName:@"pickImage"];
}

JS 调用示例:


window.webkit.messageHandlers.share.postMessage(xxx);

Native 调用 JS

Native 调用 JS 相对简单,只需 H5 将 JS 方法暴露给 Window 对象,供 Native 端调用即可。

在 Android 中,主要有两种实现方式。4.4 版本以前,通过 loadUrl 方法执行一段 JS 代码来实现;4.4 版本以后,可使用 evaluateJavascript 方法。loadUrl 方法使用便捷,但效率较低,无法获取返回值,且调用时会刷新 WebView。evaluateJavascript 方法效率高,可方便获取返回值,调用时不刷新 WebView,但仅支持 Android 4.4 及以上系统。相关代码如下:


webView.loadUrl("javascript:" + javaScriptString);
webView.evaluateJavascript(javaScriptString, new ValueCallback<String>() {
  @Override
  public void onReceiveValue(String value){
    xxx
  }
});

在 iOS 的 WKWebView 中,可通过 evaluateJavaScript:javaScriptString 方法实现,支持 iOS 8.0 及以上系统。


// swift
func evaluateJavaScript(_ javaScriptString: String, 
    completionHandler: ((Any?, Error?) -> Void)? = nil)
// javaScriptString 需要调用的 JS 代码
// completionHandler 执行后的回调
// objective-c
[jsContext evaluateJavaScript:@"ZcyJsBridge(ev, data)"]

JSBridge 的使用

如何引用

  • 由 H5 引用:在我司移动端初期版本中采用此方式,通过本地引入 npm 包进行调用。这种方式可确保 JSBridge 存在,可直接调用 Native 方法。但若后期 Bridge 实现方式变更,双方需进行更多兼容处理,维护成本较高。
  • 由 Native 注入:这是当前我司移动端选用的方式。考虑到后期业务需求,重新设计后采用 Native 注入方式引用 JSBridge。这有利于保持 API 与 Native 的一致性,但缺点在于 Native 注入的方法和时机受限,JS 调用 Native 前需先判断 JSBridge 是否注入成功。

使用规范

H5 调用 Native 方法的伪代码实例,如:


params = {
  api_version: "xxx",    // API 版本
  title: "xxx",    // 标题
  filename: "xxx",    // 文件名称
  image: "xxx",    // 图片链接
  url: "xxx",    // 网址链接
  success: function (res) {
    xxx;    // 调用成功后执行
  },
  fail: function (err) {
    if (err.code == '-2') {
      fail && fail(err);    //    调用了当前客户端中不存在的 API 版本
    } else {
      const msg = err.msg;    //异常信息
      Toast.fail(msg);
    }
  }
};
window.NativeApi.share(params);

以下简要列出通用方法的抽象,目前基本遵循以下规范进行双端通信。


window.NativeApi.xxx({
    api_version:'',
    name: "xxx",
    path: "xxx",
    id:    "xxx",
    success: function (res) {
      console.log(res);
    },
    fail: function (err) {
      console.log(err);
    }
});

由于初期版本选择了由 H5 本地引用 JSBridge,后期采用 Native 注入的方式。现有的 H5 需要对各种情况做兼容,逻辑抽象如下:


reqNativeBridge(vm, fn) {
  if (!isApp()) {
    // 如果不在 APP 内进行调用
    vm.$dialog.alert({
      message: "此功能需要访问 APP 才能使用",
    });
  } else {
    if (!window.NativeApi) {
      // 针对初期版本
      vm.$dialog.alert({
        message: "请更新到最新 APP 使用该功能",
      });
    } else {
      // 此处只针对“调用了当前客户端中不存在的 API 版本”的报错进行处理
      // 其余种类的错误信息交由具体的业务去处理
      fn && fn((err) => {
        vm.$dialog.alert({
          message: "请更新到最新 APP 使用该功能",
        });
      });
    }
  }
}

总结

上述内容简要介绍了 JSBridge 的部分原理,希望对从未了解过 JSBridge 的同学能有所帮助。如果需要更深入地了解 JSBridge 的原理和实现,如 JSBridge 接口调用的封装实现、JS 调用 Native 时的回调的唯一性等,大家可以去查阅更多资料,参考更详细的相关文档或他人的整理成文的沉淀。

以上就是Javascript之JSBridge初探的详细内容,更多关于Javascript之JSBridge的资料请关注本站其它相关文章!

作者最新文章