创新型的顺的网站制作,郑州餐饮网站建设公司排名,贵阳新闻最新消息今天,做校园文化展览的网站1. 概述#xff1a;
在混合应用开发中#xff0c;一种常见且成熟的技术方案是将原生应用与 WebView 结合#xff0c;使得复杂的业务逻辑可以通过网页技术实现。实现这种类型的混合应用时#xff0c;就需要解决H5与Native之间的双向通信。JSBridge 是一种在混合应用中实现 …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端双向通信的一种机制。
通过JSBridgeWeb端可以调用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 ValueCallbackString() {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?texthello
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_typexxxxselected_tabxxxxx;} else if (this.env.isSFCApp) {url hellohitch://hellohitch.com/xxx/xxxx?bottomTabxxxx;}return url;}
这种方式从早期就存在兼容性很好但是由于是基于URL的方式长度受到限制而且不太直观数据格式有限制而且建立请求有时间耗时。
3.2.2. 在Webview中注入JS API
通过webView提供的接口App将Native的相关接口注入到JS的Contextwindow的对象中
Web端就可以直接在全局 window 下使用这个暴露的全局JS对象进而调用原生端的方法。 Android注入方法
4.2 前Android 注入 JavaScript 对象的接口是 addJavascriptInterface 但是这个接口有漏洞4.2 之后Android引入新的接口 JavascriptInterface 以解决安全问题所以 Android 注入对对象的方式是有兼容性问题的。 IOS注入方法
iOS的UIWebViewJavaSciptCore 支持 iOS 7.0 及以上系统iOS的WKWebViewWKScriptMessageHandler 支持 iOS 8.0 及以上系统 例如
注入全局对象
// 注入全局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();}
}Web调用方法
// 调用nativeBridge的方法
window.NativeBridge.showNativeDialog(hello); 4. H5具体实现
将功能抽象为一个 AppBridge 类封装两个方法处理交互和回调
具体步骤
首先需要定义一个 JavaScript 类或者对象来封装 JSBridge 方法。在 JavaScript 类或对象的构造函数中初始化桥接回调的方法。这个方法负责接收来自原生应用的回调数据并根据回调数据中的信息执行相应的操作。调用原生方法 定义一个方法用于在 JavaScript 中调用原生方法。这个方法需要接收原生类的映射、要调用的原生方法名以及传递给原生方法的参数并将这些信息传递给原生应用。处理原生回调 在初始化桥接回调的方法中需要定义处理原生回调的逻辑。当收到原生应用的回调数据时根据回调数据中的信息执行相应的操作比如调用 JavaScript 中注册的回调函数并传递执行结果或错误信息等。 具体实现代码
调用原生方法
// 定义一个名为 callNative 的方法用于在 JavaScript 中调用原生方法
callNativeP, R(classMap: string, method: string, params: P): PromiseR {return new PromiseR((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);}});
}回调处理
// 初始化桥接回调函数该参数在 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];}};
}使用
// 调用原生方法的封装函数
callNativeP, R(classMap: string, method: string, params: P) {// 从容器中解析出 AppBridge 实例const bridge container.resolveAppBridge(AppBridge);// 使用 bind 方法将 AppBridge 实例中的 callNative 方法绑定到 bridge 对象上并保存到 func 变量中const func bridge.callNative.bind(bridge);// 调用 func 方法并传入 classMap、method 和 params 参数实现调用原生方法的功能return funcP, R(classMap, method, params);
}// 打开 webview
// 调用 callNative 方法传入参数 urlclassMap 为 xxxxx/hitchmethod 为 openWebview
openWebView(url: string): Promisevoid {return this.callNative{url:string}, void(xxxxx/hitch, openWebview, { url });
}// 获取驾驶证 OCR 信息
getDriverLicenseOcrInfo(params: HBNative.getDriverLicenseOcrInfo.Params,
): PromiseHBNative.getDriverLicenseOcrInfo.Result {// 调用 callNative 方法传入参数 paramsclassMap 为 xxxxx/hitchmethod 为 getOcrInfo// 返回一个 Promise 对象该 Promise 对象用于处理异步结果return this.callNativeHBNative.getDriverLicenseOcrInfo.Params,HBNative.getDriverLicenseOcrInfo.Result(xxxxx/hitch, getOcrInfo, params,);
}