JSBridge

news2024/11/25 18:11:35

在Hybrid模式下,H5会经常需要使用Native的功能,比如打开二维码扫描、调用原生页面、获取用户信息等,同时Native也需要向Web端发送推送、更新状态等,而JavaScript是运行在单独的JS Context中(Webview容器、JSCore等),与原生有运行环境的隔离,所以需要有一种机制实现Native端和Web端的双向通信,这就是JSBridge:以JavaScript引擎或Webview容器作为媒介,通过协定协议进行通信,实现Native端和Web端双向通信的一种机制。

通过JSBridge,Web端可以调用Native端的Java接口,同样Native端也可以通过JSBridge调用Web端的JavaScript接口,实现彼此的双向调用

三、WebView

首先了解下webView,webView是移动端提供的运行JavaScript的环境,是系统渲染Web网页的一个控件,可与页面JavaScript交互,实现混合开发,其中Android和iOS又有些不同:

Android的WebView采用的是低版本和高版本使用了不同的webkit内核,4.4后直接使用了Chrome

iOS中UIWebView算是自IOS2就有,但性能较差,特性支持较差,WKWebView是iOS8之后的升级版,性能更强特性支持也较好。

WebView控件除了能加载指定的url外,还可以对URL请求、JavaScript的对话框、加载进度、页面交互进行强大的处理,之后会提到拦截请求、执行JS脚本都依赖于此。

四、JSB实现原理

Web端和Native可以类比于Client/Server模式,Web端调用原生接口时就如同Client向Server端发送一个请求类似,JSB在此充当类似于HTTP协议的角色,实现JSBridge主要是两点:

  1. 将Native端原生接口封装成JavaScript接口
  2. 将Web端JavaScript接口封装成原生接口

4.1 Native->Web

首先来说Native端调用Web端,这个比较简单,JavaScript作为解释性语言,最大的一个特性就是可以随时随地地通过解释器执行一段JS代码,所以可以将拼接的JavaScript代码字符串,传入JS解析器执行就可以,JS解析器在这里就是webView。

Android 4.4之前只能用loadUrl来实现,并且无法执行回调:

String jsCode = String.format("window.showWebDialog('%s')", text);
webView.loadUrl("javascript: " + jsCode);

Android 4.4之后提供了evaluateJavascript来执行JS代码,并且可以获取返回值执行回调:

String jsCode = String.format("window.showWebDialog('%s')", text);
webView.evaluateJavascript(jsCode, new ValueCallback<String>() {
  @Override
  public void onReceiveValue(String value) {

  }
});

iOS的UIWebView使用stringByEvaluatingJavaScriptFromString

NSString *jsStr = @"执行的JS代码";
[webView stringByEvaluatingJavaScriptFromString:jsStr];

iOS的WKWebView使用evaluateJavaScript

[webView evaluateJavaScript:@"执行的JS代码" completionHandler:^(id _Nullable response, NSError * _Nullable error) {

}];

4.2 Web->Native

Web调用Native端主要有两种方式

4.2.1 拦截Webview请求的URL Schema

URL Schema是类URL的一种请求格式,格式如下:

<protocol>://<host>/<path>?<qeury>#fragment

我们可以自定义JSBridge通信的URL Schema,比如:jsbridge://showToast?text=hello

Native加载WebView之后,Web发送的所有请求都会经过WebView组件,所以Native可以重写WebView里的方法,从来拦截Web发起的请求,我们对请求的格式进行判断:

  • 如果符合我们自定义的URL Schema,对URL进行解析,拿到相关操作、操作,进而调用原生Native的方法
  • 如果不符合我们自定义的URL Schema,我们直接转发,请求真正的服务

Web发送URL请求的方法有这么几种:

  1. a标签
  2. location.href
  3. 使用iframe.src
  4. 发送ajax请求

这些方法,a标签需要用户操作,location.href可能会引起页面的跳转丢失调用,发送ajax请求Android没有相应的拦截方法,所以使用iframe.src是经常会使用的方案:

  • 安卓提供了shouldOverrideUrlLoading方法拦截
  • UIWebView使用shouldStartLoadWithRequest,WKWebView则使用decidePolicyForNavigationAction

这种方式从早期就存在,兼容性很好,但是由于是基于URL的方式,长度受到限制而且不太直观,数据格式有限制,而且建立请求有时间耗时。

4.2.2 向Webview中注入JS API

这个方法会通过webView提供的接口,App将Native的相关接口注入到JS的Context(window)的对象中,一般来说这个对象内的方法名与Native相关方法名是相同的,Web端就可以直接在全局window下使用这个暴露的全局JS对象,进而调用原生端的方法。

这个过程会更加简单直观,不过有兼容性问题,大多数情况下都会使用这种方式

Android(4.2+)提供了addJavascriptInterface注入:

// 注入全局JS对象
webView.addJavascriptInterface(new NativeBridge(this), "NativeBridge");

class NativeBridge {
  private Context ctx;
  NativeBridge(Context ctx) {
    this.ctx = ctx;
  }

  // 增加JS调用接口
  @JavascriptInterface
  public void showNativeDialog(String text) {
    new AlertDialog.Builder(ctx).setMessage(text).create().show();
  }
}

在Web端直接调用这个方法即可:

window.NativeBridge.showNativeDialog('hello');

iOS的UIWebView提供了JavaSciptCore

iOS的WKWebView提供了WKScriptMessageHandler

4.3 带回调的调用

上面已经说到了Native、Web间双向通信的两种方法,但站在一端而言还是一个单向通信的过程 ,比如站在Web的角度:Web调用Native的方法,Native直接相关操作但无法将结果返回给Web,但实际使用中会经常需要将操作的结果返回,也就是JS回调。

所以在对端操作并返回结果,有输入有输出才是完整的调用,那如何实现呢?

其实基于之前的单向通信就可以实现,我们在一端调用的时候在参数中加一个callbackId标记对应的回调,对端接收到调用请求后,进行实际操作,如果带有callbackId,对端再进行一次调用,将结果、callbackId回传回来,这端根据callbackId匹配相应的回调,将结果传入执行就可以了。

可以看到实际上还是通过两次单项通信实现的。

以Android,在Web端实现带有回调的JSB调用为例:

// Web端代码:
<body>
  <div>
    <button id="showBtn">获取Native输入,以Web弹窗展现</button>
  </div>
</body>
<script>
  let id = 1;
  // 根据id保存callback
  const callbackMap = {};
  // 使用JSSDK封装调用与Native通信的事件,避免过多的污染全局环境
  window.JSSDK = {
    // 获取Native端输入框value,带有回调
    getNativeEditTextValue(callback) {
      const callbackId = id++;
      callbackMap[callbackId] = callback;
      // 调用JSB方法,并将callbackId传入
      window.NativeBridge.getNativeEditTextValue(callbackId);
    },
    // 接收Native端传来的callbackId
    receiveMessage(callbackId, value) {
      if (callbackMap[callbackId]) {
        // 根据ID匹配callback,并执行
        callbackMap[callbackId](value);
      }
    }
  };

    const showBtn = document.querySelector('#showBtn');
  // 绑定按钮事件
  showBtn.addEventListener('click', e => {
    // 通过JSSDK调用,将回调函数传入
    window.JSSDK.getNativeEditTextValue(value => window.alert('Natvie输入值:' + value));
  });
</script>
// Android端代码
webView.addJavascriptInterface(new NativeBridge(this), "NativeBridge");

class NativeBridge {
  private Context ctx;
  NativeBridge(Context ctx) {
    this.ctx = ctx;
  }

  // 获取Native端输入值
  @JavascriptInterface
  public void getNativeEditTextValue(int callbackId) {
    MainActivity mainActivity = (MainActivity)ctx;
    // 获取Native端输入框的value
    String value = mainActivity.editText.getText().toString();
    // 需要注入在Web执行的JS代码
    String jsCode = String.format("window.JSSDK.receiveMessage(%s, '%s')", callbackId, value);
    // 在UI线程中执行
    mainActivity.runOnUiThread(new Runnable() {
      @Override
      public void run() {
        mainActivity.webView.evaluateJavascript(jsCode, null);
      }
    });
  }
}

以上代码简单实现了一个demo,在Web端点击按钮,会获取Native端输入框的值,并将值以Web端弹窗展现,这样就实现了Web->Native带有回调的JSB调用,同理Native->Web也是同样的逻辑,不同的只是将callback保存在Native端罢了,在此就不详细论述了。

五、开源的JSBridge

可以看到,实现一个完整的JSBridge还是挺麻烦的,还需要考虑低端机型的兼容问题、同步异步调用问题,好在已经有开源的JSBridge供我们直接使用了:

  • DSBridge,主要通过注入API的形式,DSBridge for Android、DSBridge for IOS
  • JsBridge,主要通过拦截URL Schema,JsBridge

DSBridge-Android为例:

// Web端代码
<body>
  <div>
    <button id="showBtn">获取Native输入,以Web弹窗展现</button>
  </div>
</body>
// 引入SDK
<script src="https://unpkg.com/dsbridge@3.1.3/dist/dsbridge.js"></script>
<script>
  const showBtn = document.querySelector('#showBtn');
  showBtn.addEventListener('click', e => {
    // 注意,这里代码不同:SDK在全局注册了dsBridge,通过call调用Native方法
    dsBridge.call('getNativeEditTextValue', '', value => {
      window.alert('Native输入值' + value);
    })
  });
</script>
// Android代码
// 使用dwebView替换原生webView
dwebView.addJavascriptObject(new JsApi(), null);

class JSApi {
  private Context ctx;
  public JSApi (Context ctx) {
    this.ctx = ctx;
  }

  @JavascriptInterface
  public void getNativeEditTextValue(Object msg, CompletionHandler<String> handler) {
    String value = ((MainActivity)ctx).editText.getText().toString();
    // 通过handler将value传给Web端,实现回调的JSB调用
    handler.completed(value);
  }
}

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/647793.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

mysql select是如何一步步执行的呢?

mysql select执行流程如图所示 server侧 在8.0之前server存在查询语句对应数据的缓存&#xff0c;不过在实际使用中比较鸡肋&#xff0c;对于更新比较频繁、稍微改点查询语句都会导致缓存无法用到 解析 解析sql语句为mysql能够直接执行的形式。通过词法分析识别表名、字段名等…

IIC总线实验

IIC总线实验 一、IIC总线基础概念 1、I2C总线是PHLIPS公司在八十年代初推出的一种同步串行半双工总线&#xff0c;主要用于连接整体电路。 2、I2C总线为两线制&#xff0c;只有两根双向信号线 3、一根是数据线SDA&#xff0c;另一根是时钟线SCL 4、I2C硬件结构简单&#xf…

第四章 模型篇:模型训练与示例

文章目录 SummaryAutogradFunctions ()GradientBackward() OptimizationOptimization loopOptimizerLearning Rate SchedulesTime-dependent schedulesPerformance-dependent schedulesTraining with MomentumAdaptive learning rates optim.lr_scheluder Summary 在pytorch_t…

一分钟学一个 Linux 命令 - find 和 grep

前言 大家好&#xff0c;我是 god23bin。欢迎来到《一分钟学一个 Linux 命令》系列&#xff0c;每天只需一分钟&#xff0c;记住一个 Linux 命令不成问题。今天需要你花两分钟时间来学习下&#xff0c;因为今天要介绍的是两个常用的搜索命令&#xff1a;find 和 grep 命令。 …

Spring是什么?

目录 1、Spring的简介 2、Spring七大功能模块 3、Spring的优点 4、Spring的缺点 5、Sprig容器 6、Spring的生态圈&#xff08;重点&#xff09;***** 7、Spring中bean的生命周期 1、Spring的简介 Spring的英文翻译为春天&#xff0c;可以说是给Java程序员带来了春天&…

认识泛型

目录 什么是泛型 引出泛型 语法 泛型类的使用 语法 示例 类型推导(Type Inference) 裸类型(Raw Type) 小结&#xff1a; 泛型如何编译的 擦除机制 为什么不能实例化泛型类型数组 泛型方法 定义语法 泛型接口 泛型数组 什么是泛型 一般的类和方法&#xff0c…

013:解决vue中不能加载.geojson的问题

第013个 查看专栏目录: VUE — element UI 本文章目录 问题状态造成这个结果的原因&#xff1a;解决办法Vue Loader 其他特性&#xff1a;专栏目标 问题状态 在做vue项目的时候&#xff0c;碰到这样一个问题&#xff0c;vue页面中引用一个.geojson文件&#xff0c;提示如下错误…

Redis-原生命令

string 单值 set key value get key 对象 set user:1 value Mset user:1:name zhangsan user:1:sex man Mget user:1:name user:1:sex 分布式锁 setnx product:1001 true 计数器/全局序列号维护 incr article:readcount:{文章id} get article:readcount:{文章id} 哈希hash…

JavaEE语法第一章、计算机工作原理

【计算机科学速成课】[40集全/精校] - Crash Course Computer Science_哔哩哔哩_bilibili 目录 一、计算机发展史 二、冯诺依曼体系&#xff08;Von Neumann Architecture&#xff09; 三、CPU简单介绍 3.1CPU介绍 3.2并行和并发 四、操作系统&#xff08;Operating Syste…

【netty基础四】netty与nio

文章目录 一. 反应堆1. 堵塞模型2. Java NIO的工作原理 二. Netty与NIO 一. 反应堆 1. 堵塞模型 阻塞I/O在调用InputStream.read()方法时是阻塞的&#xff0c;它会一直等到数据到来&#xff08;或超时&#xff09;时才会返回&#xff1b; 同样&#xff0c;在调用ServerSocke…

PN7160 card emulation

AN13861.pdf 1 简介 本文档的目的是举例说明如何为特定的 CE 场景正确设置卡仿真 (CE)。 有关 CE 体系结构的详细说明&#xff0c;请查看用户手册 [5]。 卡仿真的硬件设置&#xff0c;参考[13]和[14]。 要求&#xff1a; • MCUXpresso 和/或Android 和/或Linux 的知识 • PN…

[游戏开发]Unity多边形分割为三角形_耳切法

[ 目录 ] 0. 前言1. 耳切法&#xff08;1&#xff09;基础的概念&#xff08;2&#xff09;耳点判断&#xff08;3&#xff09;判断角度类型&#xff08;4&#xff09;点是否在三角形内&#xff08;5&#xff09;判断顺逆时针 2. 耳切法小优化3. 耳切法实现&#xff08;1&#…

openGauss5 企业版之常用运维命令

文章目录 日维护检查项检查openGauss状态检查锁信息统计事件数据对象检查SQL报告检查备份基本信息检查 检查操作系统参数检查办法异常处理 检查openGauss健康状态检查办法 本章节主要介绍在 openGauss数据库 在日常运维中的常用命令 日维护检查项 检查openGauss状态 通过open…

Java性能权威指南-总结13

Java性能权威指南-总结13 堆内存最佳实践减少内存使用减少对象大小延迟初始化 堆内存最佳实践 减少内存使用 减少对象大小 对象会占用一定数量的堆内存&#xff0c;所以要减少内存使用&#xff0c;最简单的方式就是让对象小一些。考虑运行程序的机器的内存限制&#xff0c;增…

Nautilus Chain测试网迎阶段性里程碑,模块化区块链拉开新序幕

Nautilus Chain 是目前行业内少有的真实实践的 Layer3 模块化链&#xff0c;该链曾在几个月前上线了测试网&#xff0c;并接受用户测试交互。该链目前正处于测试网阶段&#xff0c;并即将在不久上线主网&#xff0c;这也将是行业内首个正式上线的模块化区块链底层。 而在上个月…

Android 13(T) Media框架 -异步消息机制

网上有许多优秀的博文讲解了Android的异步消息机制&#xff08;ALooper/AHandler/AMessage那一套&#xff09;&#xff0c;希望看详细代码流程的小伙伴可以去网上搜索。这篇笔记将会记录我对android异步消息机制的理解&#xff0c;这次学完之后就可以熟练运用这套异步消息机制了…

【数据库二】数据库用户管理与授权

数据库用户管理与授权 1.MySQL数据库管理1.1 常用的数据类型1.2 char和varchar区别1.3 SQL语句分类 2.数据表高级操作2.1 克隆表2.2 清空表2.3 创建临时表 3.MySQL的六大约束4.外键约束4.1 外键概述4.2 创建主从表4.3 主从表中插入数据4.4 主从表中删除数据4.5 删除外键约束 5.…

conda环境中配置cuda+cudnn+pytorch深度学习环境

本文参考&#xff1a; 在conda虚拟环境中配置cudacudnnpytorch深度学习环境&#xff08;新手必看&#xff01;简单可行&#xff01;&#xff09;_conda安装cudnn_江江ahh的博客-CSDN博客 一、创建虚拟环境 conda create -n mytorch python3.8 二、执行sudo nvidia-smi查看CU…

物联网通信技术

通信的技术指标是什么&#xff1f;AB A. 可靠性 B. 有效性 C. 实时性D. 广覆盖 多路复用技术有哪些&#xff1f;ABCD A. FDMA B. CDMA C. SDMA D. TDMA 使用多个频率来传输信号的技术被称为扩展频谱技术&#xff0c;该技术使用的目的是什么&#xff1f; AB A. 抗干扰B. 提…

【VMware】VMware安装CentOS8-Stream虚拟机

本文首发于 慕雪的寒舍 VMware安装CentOS8-Stream虚拟机 1.安装VMware 由于最新版的vm要钱&#xff0c;这里提供一个VMware16pro的安装包&#xff1b;我知道度盘下载速度慢&#xff0c;但确实没啥其他选择&#xff0c;见谅。 后文将用vm来简称VMware 提取嘛: gdt9 亚索包解…