Javascript之JSBridge初探

目录
  • JSBridge 的起源
  • JSBridge 的双向通信原理
    • JS 调用 Native
    • Native 调用 JS
  • 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.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的资料请关注我们其它相关文章!

(0)

相关推荐

  • 微信浏览器内置JavaScript对象WeixinJSBridge使用实例

    微信公众平台开始支持前端网页,大家可能看到很多网页上都有分享到朋友圈,关注微信等按钮,点击它们都会弹出一个窗口让你分享和关注,这个是怎么实现的呢?今天就给大家讲解下如何在微信公众平台前端网页上添加分享到朋友圈,关注微信号等按钮. 一.微信内置浏览器 通过 Mac 远程调试 iPhone 上微信自己的网页,我们可以发现微信内嵌浏览器定义了一个私有 JavaScript 对象:WeixinJSBridge,通过操作这个对象的相关方法可以实现分享到微信朋友圈,和判断一个微信号的关注状态以及实现关注指定

  • android和js的交互之jsbridge使用教程

    前言 众所周知,app的一些功能可能会使用到H5开发,这就难免会遇到java与js 的相互调用,android 利用WebViewJavascriptBridge 实现js和java的交互,这里介绍下JsBridge第三方库的使用. github传送门:https://github.com/lzyzsd/JsBridge  (本地下载) 简单分析 java与js相互调用如下: java发送数据给js,js接收并回传给java 同理,js发送数据给java,java接收并回传给js 同时两套流程都

  • Android中极简的js与java的交互库(SimpleJavaJsBridge)

    前言 最近接触android中js与java交互的东西很多,当然它们之间的交互方式有几种,但是我觉得这几种交互方式都存在一定的不足,这是我决定编写SimpleJavaJsBridge这个库的关键原因. 我会按以下顺序进行本文章: 现有js与java通信方案及不足 js与java完美通信方案设计 SimpleJavaJsBridge 现在进入正题 1. 现有js与java通信方案及不足 先来说明一点js与java通信,指的是js既可以给java发送消息,同时java也可以给js发送消息.那就来屡屡

  • Flutter使用JsBridge方式处理Webview与H5通信的方法

    目前,移动跨平台开发作为移动开发的重要组成部分,是移动开发者必须掌握的技能,也是自我提升的重要手段.作为Google推出的跨平台技术方案,Flutter具有诸多的优势,已经或正在被广大开发者应用在移动应用开发中.在过去的2019年,我看到越来越多的公司和个人开始使用Flutter来开发跨平台应用,对于移动应用开发来说,Flutter能够满足几乎所有的业务开发需求,所以,学习Flutter正当时. 众所周知,使用Flutter进行项目开发时,就免不了要加载H5页面,在移动开发中打开H5页面需要使用

  • 如何通过Proxy实现JSBridge模块化封装

    最近公司在做一个项目,通过把我们自己的Webview植入第三方APP,然后我们的业务全部通过H5实现.至于为什么不直接用第三方APP WebView,主要是身处金融行业,需要做一些风控相关功能. 由于是Hybrid APP的性质,所以web与Native的通信是无法避免的:而为什么我要封装jsBridge,主要在于下面两点: 公司APP的JSBridge提供了数据的序列化和全局函数的注入,而我们这次由于包大小考虑,这一块需要H5自己来实现: 原生提供的接口协议太多,记住麻烦: 回调的写法不太人性

  • JavaScript跨平台的开源框架NativeScript

    NativeScript是一款使用JavaScript语言来构建跨平台原生移动应用的开源框架,支持iOS.Android和Windows Phone.且NativeScript的使用没有过多繁杂的要求,只需使用自己已经掌握的JavaScript和CSS技能就能开发出真正具有原生用户体验的移动应用. 作为免费开源项目的NativeScript,它的源码已经托管至Github上,让开发者可以没有任何门槛约束的随意使用.除了无需学习新的编程语言,使用大家所熟识的JavaScript编码及CSS打造应用

  • 微信内置浏览器私有接口WeixinJSBridge介绍

    微信网页进入,右上角有三个小点,没错,我们用到的就是它!我们只要通过将小点列表下的按钮进行自定义,就可以随心所欲的分享我们自己的内容了. 注意:(WeixinJSBridge只能在微信内打开的网页有效) 按钮一之------发送给好友 复制代码 代码如下: function sendMessage(){    WeixinJSBridge.on('menu:share:appmessage', function(argv){ alert("发送给好友"); });   } 这样,只要在

  • native.js获取手机硬件基本信息实例代码android版

    为大家分享一些android公共方法native.js实现代代码,如获取手机MAC地址,手机内存大小,手机存储空间大小,手机CPU信息等手机硬件基本信息 native.js获取手机MAC地址 /*得到手机MAC地址*/ function getMac() { var mac = "xxx-xxx-xxx-xxx"; if (plus.os.name == "Android") { //WifiManager var Context = plus.android.im

  • 微信WeixinJSBridge API使用实例

    注意:请在微信中测试 <!DOCTYPE html> <html> <head> <title>微信WeixinJSBridge API</title> <meta charset="utf-8" /> <script type="text/javascript"> (function(){ var a=document.getElementsByTagName("html

  • Javascript之JSBridge初探

    目录 JSBridge 的起源 JSBridge 的双向通信原理 JS 调用 Native Native 调用 JS JSBridge 的使用 总结 JSBridge 的起源 近些年,移动端普及化越来越高,开发过程中选用 Native 还是 H5 一直是热门话题.Native 和 H5 都有着各自的优缺点,为了满足业务的需要,公司实际项目的开发过程中往往会融合两者进行 Hybrid 开发.Native 和 H5 分处两地,看起来无法联系,那么如何才能让双方协同实现功能呢? 这时我们想到了 Cod

  • javascript 自定义事件初探

    还有,"通过事件机制,可以将类设计为独立的模块,通过事件对外通信,提高了程序的开发效率.".相信C#程序员对事件的好处是深有体会的.好了,Code is cheap.看代码: function class1() { // 最简单的事件设计模式 } class1.prototype = { show: function () { this .onShow(); }, onShow: function () { } } function test() { var obj = new cla

  • 由prototype_1.3.1进入javascript殿堂-类的初探

    还是决定冠上ajax的头衔,毕竟很多人会用这个关键词搜索.虽然我认为这只是个炒作的概念,不过不得不承认ajax叫起来要方便多了.ajax的意思我就不详细解释了. 写这个教程的起因很简单:经过一段时间的ajax学习,有一些体会,并且越发认识到ajax技术的强大,所以决定记录下来,顺便也是对自己思路的整理. 前几年,javascript在一般人眼里,功能还很狭窄,所能做的要么是一些简单的表单验证,要么是许多华而不实的网页特效.随着flash的出现,大家已经不像以前那样热衷于js特效.似乎js能做的事

  • javascript中的事件代理初探

    事件在javascript中一直是最强大的对象之一.javascript提供了addEventListener和attachEvent两个方法来为DOM节点绑定事件,jquery作了进一步封装,提供了兼容各个浏览器的bind方法.现在来看,这种传统的事件绑定方式存在着以下不足: 1.可能需要绑定很多的EventHander. 假如页面中某个表有100行,现在必须为每一行绑定一个click事件.那么就必须绑定100个EventHandler,这对页面性能来说有着极大的负担,因为需要创建更多的内存来

  • 初探JavaScript 面向对象(推荐)

    类的声明 1. 构造函数 function Animal() { this.name = 'name' } // 实例化 new Animal() 2. ES6 class class Animal { constructor() { this.name = 'name' } } // 实例化 new Animal() 类的继承 1. 借助构造函数实现继承 原理:改变子类运行时的 this 指向,但是父类原型链上的属性并没有被继承,是不完全的继承 function Parent() { this

  • JavaScript设计模式初探

    目的:设计模式众多,尝试记录下学到的不同设计模式的优劣,方便以后查阅. 前言:半年前看高程的时候看到设计模式这章,云里雾里,不是看不明白,而是不明白为啥要如此麻烦只为创建一个对象.直到最近完成了自己第一个小项目,才体会到当代码量多起来时没有适当的规范与限制是多么大的灾难.于是重新翻开高程,总结下几种我学到的简单设计模式的优劣. 正文:本文一共介绍7种设计模式以及他们的应用场景.优劣. 1.工厂模式 直接用函数来封装对象,将对象作为返回值. function person (name,age) {

  • Javascript 面向对象编程(coolshell)

    这两天有个前同事总在问我Javascript面向对象的东西,所以,索性写篇文章让他看去吧,这里这篇文章主要想从一个整体的角度来说明一下Javascript的面向对象的编程.(成文比较仓促,应该有不准确或是有误的地方,请大家批评指正) 另,这篇文章主要基于 ECMAScript 5, 旨在介绍新技术.关于兼容性的东西,请看最后一节. 初探 我们知道Javascript中的变量定义基本如下: 复制代码 代码如下: var name = 'Chen Hao';; var email = 'haoel(

  • 初探nodeJS

    一.node概要 对nodeJS早有耳闻,但是一直迟迟没有对它下手,哈哈哈,今儿咱就来初探一下它. nodeJS是个啥东东? nodeJS,我的理解就是可以运行在后端的JavaScript. 为什么它能够在后端运行呢? 这就得归功于V8引擎(V8是Google Chrome浏览器的JavaScript引擎),通过对高性能V8引擎的封装,并通过一系列优化的API类库,使其就能够在后端运行了. 并且node有两大特点: 1.基于事件驱动: 2.无阻塞. 从而nodeJS非常适合处理并发请求. 大家都

  • JS中SetTimeout和SetInterval使用初探

    一切尽在代码截图中,愿意和大家一起成长!可以喷,毕竟是菜鸟!  好了,废话不多说了,看具体实例代码吧! <!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <title>Title</title> </head> <body> <div class="wraper">

  • 浏览器环境下JavaScript脚本加载与执行探析之动态脚本与Ajax脚本注入

    在<浏览器环境下JavaScript脚本加载与执行探析之defer与async特性>中,我们研究了延迟脚本(defer)和异步脚本(async)的执行时机.浏览器支持情况.浏览器bug以及其他的细节问题.而除了defer和async特性,动态脚本和Ajax脚本注入也是两种常用的创建无阻塞脚本的方法.总的来看,这两种方法都能达到脚本加载不影响页面解析和渲染的作用,但是在不同的浏览器中,这两种技术所创建的脚本的执行时机还是有一定差异,今天我们再来探讨一下通过动态脚本技术和Ajax注入的脚本在这些方

随机推荐