JSBridge原理 - 前端H5与客户端Native交互
suiw9 2024-10-23 18:47 45 浏览 0 评论
1. 概述:
在混合应用开发中,一种常见且成熟的技术方案是将原生应用与 WebView 结合,使得复杂的业务逻辑可以通过网页技术实现。实现这种类型的混合应用时,就需要解决H5与Native之间的双向通信。JSBridge 是一种在混合应用中实现 Web 和原生代码之间通信的重要机制。
1.1. 混和开发:
混合开发(Hybrid)是一种开发模式,指使用多种开发模型开发App,通常会涉及到两大类技术:
原生 Native、Web H5
- 原生技术主要指iOS、Android,原生开发效率较低,开发完成需要重新打包整个App,发布依赖用户的更新,性能较高功能覆盖率更高
- Web H5可以更好的实现发布更新,跨平台也更加优秀,但性能较低,特性也受限
混合开发的意义就在于吸取两者的优点,而且随着手机硬件的升级迭代、系统(Android 5.0+、ISO 9.0+)对于Web特性的较好支持,H5的劣势被逐渐缩小。
1.2. JSBridge 的概念和作用:
- 通信桥梁: JSBridge 充当了 Web 应用和原生应用之间的通信桥梁。通过 JSBridge,我们可以在 web 和原生代码之间进行双向通信,使这两者能够互相调用和传递数据。
- 原生功能调用: 使用 JSBridge,我们可以在 JavaScript 中调用原生应用中的功能。我们可以通过 web 来触发原生应用中的特定操作,如打开相机、发送通知、调用硬件设备等。
- 数据传递: JSBridge 使得 JavaScript 和原生代码之间可以方便地传递数据。意味着我们可以在 web 和原生代码之间传递复杂的数据结构,如对象、数组等,以满足应用的功能需求。
- 回调机制: JSBridge 支持回调机制,使得在原生代码执行完某些操作后可以通知 JavaScript,并传递相应的结果。
1.3. 为什么在混合应用开发中 JSBridge 如此重要:
- 跨平台开发: JSBridge 允许我们在混合应用中使用一套代码同时运行在不同的平台上。这意味着我们可以使用 Web 技术来开发应用的核心逻辑,并在需要时通过 JSBridge 调用原生功能,从而实现跨平台开发,提高开发效率。
- 原生功能扩展: 使用 JSBridge,我们可以充分利用原生平台提供的功能和能力,例如访问硬件设备、调用系统 API 等。这使得我们可以为应用添加更多丰富的功能,提升用户体验。
- 灵活性和扩展性: 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代码,并且可以获取返回值执行回调:
java复制代码String jsCode = String.format("window.showWebDialog('%s')", text);
webView.evaluateJavascript(jsCode, new ValueCallback<String>() {
@Override
public void onReceiveValue(String value) {
}
});
3.1.2. IOS:
IOS的 WKWebView 使用 evaluateJavaScript:
java复制代码[webView evaluateJavaScript:@"执行的JS代码"
completionHandler:^(id _Nullable response, NSError * _Nullable error) {
//
}];
3.2. Web -> Native
Web调用Native端主要有两种方式
3.2.1. URL Schema
URL Schema是类URL的一种请求格式,格式如下:
java复制代码<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,我们直接转发,请求真正的服务
例如:
typescript复制代码 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 及以上系统
例如:
- 注入全局对象
java复制代码// 注入全局JS对象
webView.addJavascriptInterface(new NativeBridge(this), "NativeBridge");
class NativeBridge {
private Context ctx;
NativeBridge(Context ctx) {
this.ctx = ctx;
}
// 绑定方法
@JavascriptInterface
public void showNativeDialog(String text) {
new AlertDialog.Builder(ctx).setMessage(text).create().show();
}
}
- Web调用方法:
java复制代码// 调用nativeBridge的方法
window.NativeBridge.showNativeDialog('hello');
4. H5具体实现:
将功能抽象为一个 AppBridge 类,封装两个方法,处理交互和回调
具体步骤:
- 首先需要定义一个 JavaScript 类或者对象来封装 JSBridge 方法。
- 在 JavaScript 类或对象的构造函数中,初始化桥接回调的方法。这个方法负责接收来自原生应用的回调数据,并根据回调数据中的信息执行相应的操作。
- 调用原生方法: 定义一个方法,用于在 JavaScript 中调用原生方法。这个方法需要接收原生类的映射、要调用的原生方法名以及传递给原生方法的参数,并将这些信息传递给原生应用。
- 处理原生回调: 在初始化桥接回调的方法中,需要定义处理原生回调的逻辑。当收到原生应用的回调数据时,根据回调数据中的信息执行相应的操作,比如调用 JavaScript 中注册的回调函数,并传递执行结果或错误信息等。
具体实现代码:
- 调用原生方法:
typescript复制代码// 定义一个名为 callNative 的方法,用于在 JavaScript 中调用原生方法
callNative<P, R>(classMap: string, method: string, params: P): Promise<R> {
return new Promise<R>((resolve, reject) => {
// 生成一个唯一的回调 ID
const 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);
}
});
}
- 回调处理:
typescript复制代码// 初始化桥接回调函数,该参数在 constructor 中调用
private initBridgeCallback() {
// 保存旧的回调函数到 oldCallback 变量中
const oldCallback = window.callBack;
// 重新定义 window.callBack 方法,用于处理原生应用的回调数据
window.callBack = (data) => {
// 如果存在旧的回调函数,则调用旧的回调函数
if (isFunction(oldCallback)) {
oldCallback(data);
}
// 获取原生应用的回调信息,包括数据和回调 ID
console.info('native callback', data, data.callbackId);
// 从回调数据中获取回调 ID
const { 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];
}
};
}
- 使用:
typescript复制代码// 调用原生方法的封装函数
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,
);
}
作者:Tone 链接:https://juejin.cn/post/7355117271213899776
相关推荐
- 俄罗斯的 HTTPS 也要被废了?(俄罗斯网站关闭)
-
发布该推文的ScottHelme是一名黑客,SecurityHeaders和ReportUri的创始人、Pluralsight作者、BBC常驻黑客。他表示,CAs现在似乎正在停止为俄罗斯域名颁发...
- 如何强制所有流量使用 HTTPS一网上用户
-
如何强制所有流量使用HTTPS一网上用户使用.htaccess强制流量到https的最常见方法可能是使用.htaccess重定向请求。.htaccess是一个简单的文本文件,简称为“.h...
- https和http的区别(https和http有何区别)
-
“HTTPS和HTTP都是数据传输的应用层协议,区别在于HTTPS比HTTP安全”。区别在哪里,我们接着往下看:...
- 快码住!带你十分钟搞懂HTTP与HTTPS协议及请求的区别
-
什么是协议?网络协议是计算机之间为了实现网络通信从而达成的一种“约定”或“规则”,正是因为这个“规则”的存在,不同厂商的生产设备、及不同操作系统组成的计算机之间,才可以实现通信。简单来说,计算机与网络...
- 简述HTTPS工作原理(简述https原理,以及与http的区别)
-
https是在http协议的基础上加了一层SSL(由网景公司开发),加密由ssl实现,它的目的是为用户提供对网站服务器的身份认证(需要CA),以至于保护交换数据的隐私和完整性,原理如图示。1、客户端发...
- 21、HTTPS 有几次握手和挥手?HTTPS 的原理什么是(高薪 常问)
-
HTTPS是3次握手和4次挥手,和HTTP是一样的。HTTPS的原理...
- 一次安全可靠的通信——HTTPS原理
-
为什么HTTPS协议就比HTTP安全呢?一次安全可靠的通信应该包含什么东西呢,这篇文章我会尝试讲清楚这些细节。Alice与Bob的通信...
- 为什么有的网站没有使用https(为什么有的网站点不开)
-
有的网站没有使用HTTPS的原因可能涉及多个方面,以下是.com、.top域名的一些见解:服务器性能限制:HTTPS使用公钥加密和私钥解密技术,这要求服务器具备足够的计算能力来处理加解密操作。如果服务...
- HTTPS是什么?加密原理和证书。SSL/TLS握手过程
-
秘钥的产生过程非对称加密...
- 图解HTTPS「转」(图解http 完整版 彩色版 pdf)
-
我们都知道HTTPS能够加密信息,以免敏感信息被第三方获取。所以很多银行网站或电子邮箱等等安全级别较高的服务都会采用HTTPS协议。...
- HTTP 和 HTTPS 有何不同?一文带你全面了解
-
随着互联网时代的高速发展,Web服务器和客户端之间的安全通信需求也越来越高。HTTP和HTTPS是两种广泛使用的Web通信协议。本文将介绍HTTP和HTTPS的区别,并探讨为什么HTTPS已成为We...
- HTTP与HTTPS的区别,详细介绍(http与https有什么区别)
-
HTTP与HTTPS介绍超文本传输协议HTTP协议被用于在Web浏览器和网站服务器之间传递信息,HTTP协议以明文方式发送内容,不提供任何方式的数据加密,如果攻击者截取了Web浏览器和网站服务器之间的...
- 一文让你轻松掌握 HTTPS(https详解)
-
一文让你轻松掌握HTTPS原文作者:UC国际研发泽原写在最前:欢迎你来到“UC国际技术”公众号,我们将为大家提供与客户端、服务端、算法、测试、数据、前端等相关的高质量技术文章,不限于原创与翻译。...
- 如何在Spring Boot应用程序上启用HTTPS?
-
HTTPS是HTTP的安全版本,旨在提供传输层安全性(TLS)[安全套接字层(SSL)的后继产品],这是地址栏中的挂锁图标,用于在Web服务器和浏览器之间建立加密连接。HTTPS加密每个数据包以安全方...
- 一文彻底搞明白Http以及Https(http0)
-
早期以信息发布为主的Web1.0时代,HTTP已可以满足绝大部分需要。证书费用、服务器的计算资源都比较昂贵,作为HTTP安全扩展的HTTPS,通常只应用在登录、交易等少数环境中。但随着越来越多的重要...
你 发表评论:
欢迎- 一周热门
-
-
Linux:Ubuntu22.04上安装python3.11,简单易上手
-
宝马阿布达比分公司推出独特M4升级套件,整套升级约在20万
-
MATLAB中图片保存的五种方法(一)(matlab中保存图片命令)
-
别再傻傻搞不清楚Workstation Player和Workstation Pro的区别了
-
Linux上使用tinyproxy快速搭建HTTP/HTTPS代理器
-
如何提取、修改、强刷A卡bios a卡刷bios工具
-
Element Plus 的 Dialog 组件实现点击遮罩层不关闭对话框
-
日本组合“岚”将于2020年12月31日停止团体活动
-
SpringCloud OpenFeign 使用 okhttp 发送 HTTP 请求与 HTTP/2 探索
-
tinymce 号称富文本编辑器世界第一,大家同意么?
-
- 最近发表
- 标签列表
-
- dialog.js (57)
- importnew (44)
- windows93网页版 (44)
- yii2框架的优缺点 (45)
- tinyeditor (45)
- qt5.5 (60)
- windowsserver2016镜像下载 (52)
- okhttputils (51)
- android-gif-drawable (53)
- 时间轴插件 (56)
- docker systemd (65)
- slider.js (47)
- android webview缓存 (46)
- pagination.js (59)
- loadjs (62)
- openssl1.0.2 (48)
- velocity模板引擎 (48)
- pcre library (47)
- zabbix微信报警脚本 (63)
- jnetpcap (49)
- pdfrenderer (43)
- fastutil (48)
- uinavigationcontroller (53)
- bitbucket.org (44)
- python websocket-client (47)