我的编程空间,编程开发者的网络收藏夹
学习永远不晚

Javascript之JSBridge初探

短信预约 -IT技能 免费直播动态提醒
省份

北京

  • 北京
  • 上海
  • 天津
  • 重庆
  • 河北
  • 山东
  • 辽宁
  • 黑龙江
  • 吉林
  • 甘肃
  • 青海
  • 河南
  • 江苏
  • 湖北
  • 湖南
  • 江西
  • 浙江
  • 广东
  • 云南
  • 福建
  • 海南
  • 山西
  • 四川
  • 陕西
  • 贵州
  • 安徽
  • 广西
  • 内蒙
  • 西藏
  • 新疆
  • 宁夏
  • 兵团
手机号立即预约

请填写图片验证码后获取短信验证码

看不清楚,换张图片

免费获取短信验证码

Javascript之JSBridge初探

JSBridge 的起源

近些年,移动端普及化越来越高,开发过程中选用 Native 还是 H5 一直是热门话题。Native 和 H5 都有着各自的优缺点,为了满足业务的需要,公司实际项目的开发过程中往往会融合两者进行 Hybrid 开发。Native 和 H5 分处两地,看起来无法联系,那么如何才能让双方协同实现功能呢?

这时我们想到了 Codova ,Codova 提供了一组与设备相关的 API ,是早期js调用原生代码来实现原生功能的常用方案。不过 JSBridge 真正在国内广泛应用是由于移动互联网的盛行。

JSBridge 是一种 JS 实现的 Bridge,连接着桥两端的 Native 和 H5。它在 APP 内方便地让 Native 调用 JS,JS 调用 Native ,是双向通信的通道。JSBridge 主要提供了 JS 调用 Native代码的能力,实现原生功能如查看本地相册、打开摄像头、指纹支付等。

H5 与 Native 对比

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

JSBridge 的双向通信原理

JS 调用 Native

JS 调用 Native 的实现方式较多,主要有拦截URL Scheme、重写 prompt 、注入 API 等方法。

拦截 URL Scheme

Android 和 iOS 都可以通过拦截 URL Scheme 并解析 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 页面。某一链接填写的是 H5 链接,在对应的 Native 页面开发完成前先跳转至 H5 页面,待 Native 页面开发完后再进行拦截,跳转至 Native 页面,此时 H5 的链接无需进行修改。但是使用 iframe.class="lazy" data-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];

}

使用该方式时,可以与 Android 和 iOS 约定好使用传参的格式,这样 H5 可以无需识别客户端,传入不同参数直接调用 Native 即可。剩下的交给客户端自己去拦截相同的方法,识别相同的参数,进行自己的处理逻辑即可实现多端表现一致。如:


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

另外,如果能与 Native 确定好方法名、传参等调用的协议规范,这样其它格式的 prompt 等方法是不会被识别的,能起到隔离的作用。

##### 注入 API

基于Webview提供的能力,我们可以向 Window 上注入对象或方法。JS 通过这个对象或方法进行调用时,执行对应的逻辑操作,可以直接调用 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的资料请关注编程网其它相关文章!

免责声明:

① 本站未注明“稿件来源”的信息均来自网络整理。其文字、图片和音视频稿件的所属权归原作者所有。本站收集整理出于非商业性的教育和科研之目的,并不意味着本站赞同其观点或证实其内容的真实性。仅作为临时的测试数据,供内部测试之用。本站并未授权任何人以任何方式主动获取本站任何信息。

② 本站未注明“稿件来源”的临时测试数据将在测试完成后最终做删除处理。有问题或投稿请发送至: 邮箱/279061341@qq.com QQ/279061341

Javascript之JSBridge初探

下载Word文档到电脑,方便收藏和打印~

下载Word文档

猜你喜欢

python之初探编程

接触python语言完全是个偶然。那时候还在一所培训学校里学习网络和系统,和朋友一起去图书馆淘书,转遍了整个楼层,只是找到了一两本感兴趣的计算机方面的书,朋友选的很快,开始催我回去了,真是纠结,拿着借阅卡一次可以借5本,图书馆也挺远,本想着
2023-01-31

进阶之初探nodeJS

一、前言 在"初探nodeJS"随笔中,我们对于node有了一个大致地了解,并在最后也通过一个示例,了解了如何快速地开启一个简单的服务器。 今儿,再次看了该篇随笔,发现该随笔理论知识稍多,适合初级入门node,固萌生一个想法--想在该篇随笔
2022-06-04

Golang初探:探索之路开始

【Golang简介:一切从这里开始】Golang,也被称为Go语言,是一种由Google开发的开源编程语言。自2007年开始设计,2009年正式发布,Golang以其出色的性能、简洁的语法和强大的工具链迅速赢得开发者们的青睐。作为一门静态
Golang初探:探索之路开始
2024-02-24

初探Java之旅:探寻Java的奥秘

✨个人主页:全栈程序猿的CSDN博客 💨系列专栏:Java从入门到精通 ✌座右铭:编码如诗,Bug似流星,持续追求优雅的代码,解决问题如同星辰般自如 在计算机编程的世界中,有一门被誉为“千变万化”的编程语言——Jav
初探Java之旅:探寻Java的奥秘
2023-12-23

python3爬虫初探(六)之EXCEL

在爬取数据之后,数据的保存就成为一个新的问题,一般不太大的的数据存储到EXCEL就可以了。这里介绍一个python的第三方库——xlsxwriter.这个库的安装就不介绍了,pip就可以,不用FQ。这里仅介绍一些简单常用的命令。 首先,说明
2023-01-31

Python 命令行之旅 —— 初探

『讲解开源项目系列』启动——让对开源项目感兴趣的人不再畏惧、让开源项目的发起者不再孤单。跟着我们的文章,你会发现编程的乐趣、使用和发现参与开源项目如此简单。欢迎联系我们给我们投稿,让更多人爱上开源、贡献开源~前言你是否好奇过在命令行中敲入一
2023-01-31

Python探索之Metaclass初步了解

先以一个大牛的一段关于Python Metapgramming的著名的话来做开头:Metaclasses are deeper magic than 99% of users should ever worry about. If you
2022-06-05

学习Android开发之RecyclerView使用初探

在进行一些MaterialDesign规范开发的时候,比如之前说到的CoordinateLayout实现的向上折叠效果的时候,如果依然使用ListView,那么这种效果是做不出来的,因为ListView不兼容这个控件,而替代它的就是Recy
2022-06-06

SAP MM 特殊库存之T库存初探

SAP MM 特殊库存之T库存初探笔者所在的A项目里,销售业务广泛启用了POD功能。VL02N对交货单做了发货过账后物权并没有转移,而是将自有E库存转为一个叫做在途库存的特殊库存里。等到货物到了客户那里,客户确认实际收货数量后,才去系统上执
2023-06-05

初探Oracle存储过程和函数的不同之处

初探Oracle存储过程和函数的不同之处在Oracle数据库中,存储过程(Stored Procedure)和函数(Function)是数据库中常见的两种可重复利用的代码块。虽然它们都可以封装一组SQL语句,但在使用和功能上有一些明显的区
初探Oracle存储过程和函数的不同之处
2024-03-03

掌握 JavaScript Navigator:开启网站探索之旅

掌握 JavaScript Navigator:探索网站的强大工具
掌握 JavaScript Navigator:开启网站探索之旅
2024-02-29

探索Python字符串切片之旅:从初学到专家

从入门到精通:探索Python字符串切片的奇妙世界Python是一门功能强大且易于学习的编程语言。其中一个强大的特性就是字符串切片。通过切片,我们可以轻松地在字符串中截取需要的部分,并进行各种操作。本文将引导你从入门到精通,探索Pytho
探索Python字符串切片之旅:从初学到专家
2024-02-03

JavaScript 原型模式的奥秘:探索其玄妙之处

JavaScript 原型模式是一种在对象中共享属性和方法的机制,它允许您创建对象并通过其原型链访问继承的属性和方法。这是一种在 JavaScript 中实现继承的强大工具,可以帮助您创建可重用和维护的代码。
JavaScript 原型模式的奥秘:探索其玄妙之处
2024-02-06

编程热搜

目录