JSBridge原理 - 前端H5与客户端Native交互

1. 概述:

在混合应用开发中,一种常见且成熟的技术方案是将原生应用与 WebView 结合,使得复杂的业务逻辑可以通过网页技术实现。实现这种类型的混合应用时,就需要解决H5与Native之间的双向通信。JSBridge 是一种在混合应用中实现 Web 和原生代码之间通信的重要机制。

1.1. 混和开发:

混合开发(Hybrid)是一种开发模式,指使用多种开发模型开发App,通常会涉及到两大类技术:

原生 NativeWeb H5

  • 原生技术主要指iOS、Android,原生开发效率较低,开发完成需要重新打包整个App,发布依赖用户的更新,性能较高功能覆盖率更高
  • Web H5可以更好的实现发布更新,跨平台也更加优秀,但性能较低,特性也受限

混合开发的意义就在于吸取两者的优点,而且随着手机硬件的升级迭代、系统(Android 5.0+、ISO 9.0+)对于Web特性的较好支持,H5的劣势被逐渐缩小。

1.2. JSBridge 的概念和作用:

  1. 通信桥梁: JSBridge 充当了 Web 应用和原生应用之间的通信桥梁。通过 JSBridge,我们可以在 web 和原生代码之间进行双向通信,使这两者能够互相调用和传递数据。
  2. 原生功能调用: 使用 JSBridge,我们可以在 JavaScript 中调用原生应用中的功能。我们可以通过 web 来触发原生应用中的特定操作,如打开相机、发送通知、调用硬件设备等。
  3. 数据传递: JSBridge 使得 JavaScript 和原生代码之间可以方便地传递数据。意味着我们可以在 web 和原生代码之间传递复杂的数据结构,如对象、数组等,以满足应用的功能需求。
  4. 回调机制: JSBridge 支持回调机制,使得在原生代码执行完某些操作后可以通知 JavaScript,并传递相应的结果。

1.3. 为什么在混合应用开发中 JSBridge 如此重要:

  1. 跨平台开发: JSBridge 允许我们在混合应用中使用一套代码同时运行在不同的平台上。这意味着我们可以使用 Web 技术来开发应用的核心逻辑,并在需要时通过 JSBridge 调用原生功能,从而实现跨平台开发,提高开发效率。
  2. 原生功能扩展: 使用 JSBridge,我们可以充分利用原生平台提供的功能和能力,例如访问硬件设备、调用系统 API 等。这使得我们可以为应用添加更多丰富的功能,提升用户体验。
  3. 灵活性和扩展性: JSBridge 提供了一种灵活和可扩展的方式来实现 Web 和原生代码之间的通信。开发人员可以根据应用的需求随时添加新的原生功能,并通过 JSBridge 在 JavaScript 中调用这些功能,从而实现应用的功能扩展和升级。

2. JSBridge 做了什么?

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

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

3. JSBridge 实现原理:

把 Web 端和 Native 端的通信比作 Client/Server 模式。JSBridge 充当了类似于 HTTP 协议的角色,实现了 Web 端和 Native 端之间的通信。

将 Native 端原生接口封装成 JavaScript 接口:在 Native 端将需要被调用的原生功能封装成 JavaScript 接口,让 JavaScript 代码可以调用。 JavaScript 接口会被注册到全局对象中,以供 JavaScript 代码调用。

将 Web 端 JavaScript 接口封装成原生接口: 这一步是在 Web 端将需要被调用的 JavaScript 功能封装成原生接口。这些原生接口会通过 WebView 的某些机制暴露给原生代码,以供原生代码调用。

3.1. Native -> Web

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

3.1.1. Android:

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

String jsCode = String.format("window.showWebDialog('%s')", text);
webView.evaluateJavascript(jsCode, new ValueCallback<String>() {@Overridepublic void onReceiveValue(String value) {}
});
3.1.2. IOS:

IOS的 WKWebView 使用 evaluateJavaScript:

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

3.2. Web -> Native

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

3.2.1. URL Schema

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

<protocol>://<host>/<path>?<qeury>#fragment// 我们可以自定义JSBridge通信的URL Schema,比如:
hellobike://showToast?text=hello

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

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

例如:

  get existOrderRedirect() {let url: string;if (this.env.isHelloBikeApp) {url = 'hellobike://hellobike.com/xxxxx_xxx?from_type=xxxx&selected_tab=xxxxx';} else if (this.env.isSFCApp) {url = 'hellohitch://hellohitch.com/xxx/xxxx?bottomTab=xxxx';}return url;}

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

3.2.2. 在Webview中注入JS API

通过webView提供的接口,App将Native的相关接口注入到JS的Context(window)的对象中

Web端就可以直接在全局 window 下使用这个暴露的全局JS对象,进而调用原生端的方法。

Android注入方法:

  • 4.2 前,Android 注入 JavaScript 对象的接口是 addJavascriptInterface 但是这个接口有漏洞
  • 4.2 之后,Android引入新的接口 @JavascriptInterface 以解决安全问题,所以 Android 注入对对象的方式是有兼容性问题的。

IOS注入方法:

  • iOS的UIWebView:JavaSciptCore 支持 iOS 7.0 及以上系统
  • iOS的WKWebView:WKScriptMessageHandler 支持 iOS 8.0 及以上系统

例如:

  1. 注入全局对象
// 注入全局JS对象
webView.addJavascriptInterface(new NativeBridge(this), "NativeBridge");class NativeBridge {private Context ctx;NativeBridge(Context ctx) {this.ctx = ctx;}// 绑定方法@JavascriptInterfacepublic void showNativeDialog(String text) {new AlertDialog.Builder(ctx).setMessage(text).create().show();}
}
  1. Web调用方法:
// 调用nativeBridge的方法
window.NativeBridge.showNativeDialog('hello');

4. H5具体实现:

将功能抽象为一个 AppBridge 类,封装两个方法,处理交互和回调

具体步骤:

  1. 首先需要定义一个 JavaScript 类或者对象来封装 JSBridge 方法。
  2. 在 JavaScript 类或对象的构造函数中,初始化桥接回调的方法。这个方法负责接收来自原生应用的回调数据,并根据回调数据中的信息执行相应的操作。
  3. 调用原生方法: 定义一个方法,用于在 JavaScript 中调用原生方法。这个方法需要接收原生类的映射、要调用的原生方法名以及传递给原生方法的参数,并将这些信息传递给原生应用。
  4. 处理原生回调: 在初始化桥接回调的方法中,需要定义处理原生回调的逻辑。当收到原生应用的回调数据时,根据回调数据中的信息执行相应的操作,比如调用 JavaScript 中注册的回调函数,并传递执行结果或错误信息等。

具体实现代码:

  1. 调用原生方法:
// 定义一个名为 callNative 的方法,用于在 JavaScript 中调用原生方法
callNative<P, R>(classMap: string, method: string, params: P): Promise<R> {return new Promise<R>((resolve, reject) => {// 生成一个唯一的回调 IDconst id = v4();// 将当前的回调函数保存到 __callbacks 对象中,以 callbackId 作为键this.__callbacks[id] = { resolve, reject, method: `${classMap} - ${method}` };// 构造通信数据,包括原生类映射、要调用的方法、参数和 callbackId const data = {classMap,method,params: params === null ? '' : JSON.stringify(params),callbackId: id,};const dataStr = JSON.stringify(data);// 根据当前环境判断是 iOS 还是 Android,并调用相应平台的原生方法if (this.env.isIOS && isFunction(window?.webkit?.messageHandlers?.callNative?.postMessage)) {// 如果是 iOS 平台,则调用 iOS 的原生方法window.webkit.messageHandlers.callNative.postMessage(dataStr);} else if (this.env.isAndroid && isFunction(window?.AppFunctions?.callNative)) {// 如果是 Android 平台,则调用 Android 的原生方法window.AppFunctions.callNative(dataStr);}});
}
  1. 回调处理:
// 初始化桥接回调函数,该参数在 constructor 中调用
private initBridgeCallback() {// 保存旧的回调函数到 oldCallback 变量中const oldCallback = window.callBack;// 重新定义 window.callBack 方法,用于处理原生应用的回调数据window.callBack = (data) => {// 如果存在旧的回调函数,则调用旧的回调函数if (isFunction(oldCallback)) {oldCallback(data);}// 获取原生应用的回调信息,包括数据和回调 IDconsole.info('native callback', data, data.callbackId);// 从回调数据中获取回调 IDconst { callbackId } = data;// 根据回调 ID 查找对应的回调函数const callback = this.__callbacks[callbackId];// 如果找到了对应的回调函数if (callback) {// 如果回调数据中的 code 为 0,则表示执行成功,调用 resolve 方法处理成功的结果if (data.code === 0) {callback.resolve(data.data);} else {// 否则,表示执行失败,构造一个错误对象并调用 reject 方法处理错误信息const error = new Error(data.msg) as Error & {response:unknown};error.response = data;callback.reject(error);}// 删除已经处理过的回调函数delete this.__callbacks[callbackId];}};
}
  1. 使用:
// 调用原生方法的封装函数
callNative<P, R>(classMap: string, method: string, params: P) {// 从容器中解析出 AppBridge 实例const bridge = container.resolve<AppBridge>(AppBridge);// 使用 bind 方法将 AppBridge 实例中的 callNative 方法绑定到 bridge 对象上,并保存到 func 变量中const func = bridge.callNative.bind(bridge);// 调用 func 方法,并传入 classMap、method 和 params 参数,实现调用原生方法的功能return func<P, R>(classMap, method, params);
}// 打开 webview
// 调用 callNative 方法,传入参数 url,classMap 为 'xxxxx/hitch',method 为 'openWebview'
openWebView(url: string): Promise<void> {return this.callNative<{url:string}, void>('xxxxx/hitch', 'openWebview', { url });
}// 获取驾驶证 OCR 信息
getDriverLicenseOcrInfo(params: HBNative.getDriverLicenseOcrInfo.Params,
): Promise<HBNative.getDriverLicenseOcrInfo.Result> {// 调用 callNative 方法,传入参数 params,classMap 为 'xxxxx/hitch',method 为 'getOcrInfo'// 返回一个 Promise 对象,该 Promise 对象用于处理异步结果return this.callNative<HBNative.getDriverLicenseOcrInfo.Params,HBNative.getDriverLicenseOcrInfo.Result>('xxxxx/hitch', 'getOcrInfo', params,);
}

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

如若内容造成侵权/违法违规/事实不符,请联系长河编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

uni-app(H5)论坛 | 社区 表情选择 UI组件

项目源码请移步&#xff1a;bbs 效果 实现思路 表情切换 人物、动物、小黄人不同表情之间的切换实际就是组件的切换 emoji表情 emoji表情本身就是一种字符 如需其他emoji表情可参考 EmojiAll中文官方网站 需要注意的就是数据库的存储格式需要支持emoji表情&#xff0c;我项…

使用Python将多张图片转换为动态GIF图像

在本文中&#xff0c;我们将学习如何使用Python编写代码&#xff0c;将多张静态图片转换为一个动态的GIF图像。无论你的图片格式是JPEG&#xff08;.jpg&#xff09;还是PNG&#xff08;.png&#xff09;&#xff0c;我们都将使用Python中的PIL库来实现这一功能。通过本文的学习…

证书生成和获取阿里云备案获取密钥流程

1.在java文件夹下 输入 cmd 打开命令行窗口 2. keytool -genkey -alias 证书名 -keyalg RSA -keysize 2048 -validity 36500 -keystore 证书名.keystore 输入这一行&#xff0c;把证书名三个字 改成 项目的名称&#xff08;例如&#xff1a;D23102802&#xff09; 3. 密码默认填…

【HTML】简单制作一个分形动画

目录 前言 开始 HTML部分 效果图 ​编辑​编辑​编辑​编辑总结 前言 无需多言&#xff0c;本文将详细介绍一段代码&#xff0c;具体内容如下&#xff1a; 开始 首先新建文件夹&#xff0c;创建一个文本文档&#xff0c;其中HTML的文件名改为[index.html]&a…

金融企业区域集中库的设计构想和测试验证

导读 本文探讨了金融企业区域集中库的设计构想和测试验证&#xff0c;包括架构设想、数据库整合场景测试及优势和使用设想。作者提出利用 TiDB 数据库产品集中建设区域集中库&#xff0c;解决 MySQL 存量节点的整合问题&#xff0c;实现部署的标准化、按需扩展和统一运维管理。…

gitlab使用

个人笔记&#xff08;整理不易&#xff0c;有帮助点个赞&#xff09; 笔记目录&#xff1a;学习笔记目录_pytest和unittest、airtest_weixin_42717928的博客-CSDN博客 个人随笔&#xff1a;工作总结随笔_8、以前工作中都接触过哪些类型的测试文档-CSDN博客 目录 一&#xff1a…

SSL中的CA证书

目录 一、CA概述 二、数据加密 三、身份认证 一、CA概述 SSL如何保证网络通信的安全和数据的完整性呢&#xff1f;就是采用了两种手段&#xff1a;身份认证和数据加密。身份认证就需要用到CA证书。 CA是证书的签发机构&#xff0c;它是公钥基础设施&#xff08;Public Key In…

鸿蒙OS开发实战:【自动化测试框架】使用指南

概述 为支撑HarmonyOS操作系统的自动化测试活动开展&#xff0c;我们提供了支持JS/TS语言的单元及UI测试框架&#xff0c;支持开发者针对应用接口进行单元测试&#xff0c;并且可基于UI操作进行UI自动化脚本的编写。 本指南重点介绍自动化测试框架的主要功能&#xff0c;同时…

linux centos 系统 docker及podman拉取kylin麒麟镜像内部及部署安装Gaussdb数据库

研究总结来之不易 1.首先下载安装包&#xff0c;网址&#xff1a; 软件包 | openGauss 2.参考安装连接&#xff1a; 单节点安装 openGauss学习笔记-03 openGauss极简版单节点安装_opengauss 笔记-CSDN博客 当然他们说的有些也是不完全一样的&#xff0c;根据自己的环境摸索…

.NET 设计模式—装饰器模式(Decorator Pattern)

简介 装饰者模式&#xff08;Decorator Pattern&#xff09;是一种结构型设计模式&#xff0c;它允许你在不改变对象接口的前提下&#xff0c;动态地将新行为附加到对象上。这种模式是通过创建一个包装&#xff08;或装饰&#xff09;对象&#xff0c;将要被装饰的对象包裹起来…

设计模式之命令模式讲解

概念&#xff1a;命令模式&#xff08;Command Pattern&#xff09;又称行动&#xff08;Action&#xff09;模式或交易&#xff08;Transaction&#xff09;模式。将一个请求封装成一个对象&#xff0c;从而让你使用不同的请求把客户端参数化&#xff0c;对请求排队或者记录请…

2月智能手表线上电商市场(京东天猫淘宝)分析:华为手表成最大赢家!

近年来&#xff0c;各大厂商纷纷积极布局健康管理领域&#xff0c;智能手表成为可穿戴市场的热门产品。随着越来越多的厂商进入&#xff0c;智能手表的芯片技术、显示屏技术、传感器技术等都在不断进步&#xff0c;整体性能和功能得到显著提升&#xff0c;使得用户体验更加出色…

【Labview】虚拟仪器技术

一、背景知识 1.1 虚拟仪器的定义、组成和应用 虚拟仪器的特点 虚拟仪器的突出特征为“硬件功能软件化”&#xff0c;虚拟仪器是在计算机上显示仪器面板&#xff0c;将硬件电路完成信号调理和处理功能由计算机程序完成。 虚拟仪器的组成 硬件软件 硬件是基础&#xff0c;负责将…

0104练习与思考题-算法基础-算法导论第三版

2.3-1 归并示意图 问题&#xff1a;使用图2-4作为模型&#xff0c;说明归并排序再数组 A ( 3 , 41 , 52 , 26 , 38 , 57 , 9 , 49 ) A(3,41,52,26,38,57,9,49) A(3,41,52,26,38,57,9,49)上的操作。图示&#xff1a; tips:&#xff1a;有不少在线算法可视化工具&#xff08;软…

基于taro搭建小程序多项目框架

前言 为什么需要这样一个框架&#xff0c;以及这个框架带来的好处是什么&#xff1f; 从字面意思上理解&#xff1a;该框架可以用来同时管理多个小程序&#xff0c;并且可以抽离公用组件或业务逻辑供各个小程序使用。当你工作中面临这种同时维护多个小程序的业务场景时&#…

【Mysql高可用集群-双主双活-myql+keeplived】

Mysql高可用集群-双主双活-myqlkeeplived 一、介绍二、准备工作1.两台centos7 linux服务器2.mysql安装包3.keepalived安装包 三、安装mysql1.在128、129两台服务器根据《linux安装mysql服务-两种安装方式教程》按方式一安装好mysql应用。2.修改128服务器/etc/my.cnf配置文件&am…

mynet开源库

1.介绍 个人实现的c开源网络库&#xff0e; 2.软件架构 1.结构图 2.基于event的自动分发机制 3.多优先级分发队列&#xff0c;延迟分发队列 内部event服务于通知机制的优先级为0&#xff0c;外部event优先级为1&#xff0e; 当集中处理分发的event_callback时&#xff0c…

Flutter如何集成到已有iOS工程上

大家好&#xff0c;我是咕噜铁蛋&#xff0c;今天我将和大家分享一个实用的技术教程——如何将Flutter集成到已有的iOS工程中。Flutter是Google推出的一款开源的移动UI框架&#xff0c;它允许开发者使用Dart语言来开发高性能、美观的原生应用&#xff0c;并支持iOS和Android两大…

聚观早报 | 百度文心一言上线新功能;腾势Z9GT将发布

聚观早报每日整理最值得关注的行业重点事件&#xff0c;帮助大家及时了解最新行业动态&#xff0c;每日读报&#xff0c;就读聚观365资讯简报。 整理丨Cutie 4月08日消息 百度文心一言上线新功能 腾势Z9GT将发布 华为将举办鸿蒙春季沟通会 苹果与Shutterstock达成协议 O…

LeetCode初级算法书Java题解日常更新

LeetCode初级算法高效题解&#xff08;含思路注释&#xff09; 文章目录 LeetCode初级算法高效题解&#xff08;含思路注释&#xff09;前言一、数组1.删除排序数组中的重复项2.买卖股票的最佳时机 II3.旋转数组4.存在重复元素 总结 前言 决定用四个月过一下算法 一、数组 1.…