h5页面如何与原生交互

本文讲述h5页面跟原生通信,比如在app内,调用相机,获取相册内的图片,在app内拉起微信小程序等等,h5页面没有这么多权限能够直接调用移动端的原生能力,这个时候就需要与原生进行通讯,传递一个信号给原生这边,然后原生直接调用手机端的能力。

下面分别讲解h5与Android,ios系统通信

JS与Android

Android处理

其实h5能够在app内中存在,就是在app中有webview的存在,在webview中可以是一个h5的链接,这样这个h5页面就能够app内展现。

Android中,往webview里面注入WebViewJavascriptBridge.js

image.png

我们能够看到在android往webview注入了一段js代码,接下来我们看看这段代码的逻辑。

image.png

这里往webview中的document注入事件webViewJavascriptBridgeReady,向window中添加一个对象WebViewJavascriptBridge。

注意这个WebViewJavascriptBridge对象中包含了init,send,registerHandler,callHandler,_handleMessageFormNative函数。

接下来看这个init函数中的逻辑

image.png

使用 iframe 创建消息队列的主要原因是:

  1. 实现跨域通信。iframe 可以被嵌入到不同域的页面中,使得父子页面可以跨域通信。
  2. 解耦通信方。使用 iframe,可以使得父页面和子页面的 JavaScript 环境相互隔离。父页面无法直接访问子页面的变量和函数,只能通过消息通信。这减少了两端的依赖,有利于后期维护。
  3. 兼容性好。iframe 比较老的技术,广泛支持各种浏览器,不需要担心兼容性问题。
  4. 简单易用。向 iframe 发送消息和监听消息非常简单,不需要额外的库或工具。

image.png

这里可以发现,初始化就完成了创建消息队列,初始化默认消息队列,还有最重要的发送消息。

接下来就看h5怎么接收和处理这些通信了。

h5页面处理

到这里,应该也能猜到一些了,因为上面Android注入了一段js代码,从中创建了webViewJavascriptBridgeReady事件,而且往window中添加了一个对象。

那h5这边初始化也很简单,就是判断有没有window.WebViewJavascriptBridge,有的话,就直接可以传输了,没有的话,就需要监听webViewJavascriptBridgeReady事件,当这个事件执行了,就可以进行通信了。

if (window.WebViewJavascriptBridge) {//do your work here} else {document.addEventListener('WebViewJavascriptBridgeReady', function() {//do your work here},false);}

而这个window.WebViewJavascriptBridge也就是常说的通讯桥。无论是h5调用原生方法,还是原生想要触发h5方法,都是需要使用到通讯桥。

如果未定义 window.WebViewJavascriptBridge ,则将所有 JsBridge 函数调用放入 window.WVJBCallbacks 数组中,当触发 WebViewJavascriptBridgeReady 事件时,此任务队列将被刷新。

function setupWebViewJavascriptBridge(callback) {if (window.WebViewJavascriptBridge) {return callback(WebViewJavascriptBridge);
}if (window.WVJBCallbacks) {return window.WVJBCallbacks.push(callback);
}window.WVJBCallbacks = [callback];
}

调用 setupWebViewJavascriptBridge ,然后使用桥注册处理程序或调用 Java 处理程序:

这里的registerHandler和callHandler应该很熟悉,因为在Android中注入的js代码中就有的这两个方法,这两个方法就在window.WebViewJavascriptBridge对象上。

setupWebViewJavascriptBridge(function(bridge) {bridge.registerHandler('JS to Android', function(data, responseCallback) {console.log("Android received response:", data);responseCallback(data);});bridge.callHandler('Android to JS', {'key':'value'}, function(responseData) {console.log("JS received response:", responseData);});
});

registerHandler方法是h5页面传递给Android的数据

callHandler方法是Android传递给h5的数据

总结一下工作流程:

  • JS端判断是否有window.WebViewJavascriptBridge对象;
  • 有的话就进入回调,进行通信;
  • 没有的话就需要, 将所有 JsBridge 函数调用放入 window.WVJBCallbacks 数组中,当触发 WebViewJavascriptBridgeReady 事件时,此任务队列将被刷新;

JS与IOS

IOS处理

在ios处理中,需要介绍一下WebViewJavascriptBridge

image.png

WebViewJavascriptBridge 是用于在 WKWebView,UIWebView 和 WebView 中的 Obj-C 和 JavaScript 之间发送消息的 iOS / OSX 桥接器。

这里本质跟Android是一样的,也是注入了一段js代码,然后注入到webview组件中,实现原生与js的交互。

image.png

我们先看看js处理的逻辑

image.png

这里跟Android还是很像,往window中注入一个通讯桥,通讯桥的名称是WebViewJavascriptBridge,然后在通信桥上有registerHandler,callHandler,disableJavscriptAlertBoxSafetyTimeout,_fetchQueue,_handleMessageFromObjC等方法。

接下来看一下如何往webview中注入的

image.png

与上面的Android不同的是,这是ObjC语言编写的,Android是Java语言。

// WKNavigationDelegate 协议方法,用于监听 Request 并决定是否允许导航
- (void)webView:(WKWebView *)webView decidePolicyForNavigationAction:(WKNavigationAction *)navigationAction decisionHandler:(void (^)(WKNavigationActionPolicy))decisionHandler {// webView 校验if (webView != _webView) { return; }NSURL *url = navigationAction.request.URL;__strong typeof(_webViewDelegate) strongDelegate = _webViewDelegate;// 核心代码if ([_base isWebViewJavascriptBridgeURL:url]) { // 判定 WebViewJavascriptBridgeURLif ([_base isBridgeLoadedURL:url]) { // 判定 BridgeLoadedURL// 注入 JS 代码[_base injectJavascriptFile];} else if ([_base isQueueMessageURL:url]) { // 判定 QueueMessageURL// 刷新消息队列[self WKFlushMessageQueue];} else {// 记录未知 bridge msg 日志[_base logUnkownMessage:url];}decisionHandler(WKNavigationActionPolicyCancel);return;}// 调用 _webViewDelegate 对应的代理方法if (strongDelegate && [strongDelegate respondsToSelector:@selector(webView:decidePolicyForNavigationAction:decisionHandler:)]) {[_webViewDelegate webView:webView decidePolicyForNavigationAction:navigationAction decisionHandler:decisionHandler];} else {decisionHandler(WKNavigationActionPolicyAllow);}
}

h5页面处理

这里跟Android处理不同,启动通讯桥时,在h5页面也需要创建一个iframe标签来进行通信

function setupWebViewJavascriptBridge(callback) {if (window.WebViewJavascriptBridge) { return callback(WebViewJavascriptBridge); }if (window.WVJBCallbacks) { return window.WVJBCallbacks.push(callback); }window.WVJBCallbacks = [callback];// 创建一个 iframevar WVJBIframe = document.createElement('iframe');// 设置 iframe 为不显示WVJBIframe.style.display = 'none';// 将 iframe 的 src 置为 'https://__bridge_loaded__'WVJBIframe.src = 'https://__bridge_loaded__';// 将 iframe 加入到 document.documentElementdocument.documentElement.appendChild(WVJBIframe);setTimeout(function() { document.documentElement.removeChild(WVJBIframe) }, 0)
}

Note: 假 Request 的发起有两种方式,-1:location.href -2:iframe。通过 location.href 有个问题,就是如果 JS 多次调用原生的方法也就是 location.href 的值多次变化,Native 端只能接受到最后一次请求,前面的请求会被忽略掉,所以这里 WebViewJavascriptBridge 选择使用 iframe。

因为加入了 src 为 https://__bridge_loaded__ 的 iframe 元素,我们上面截获 url 的代理方法就会拿到一个 https://__bridge_loaded__ 的 url,由于 https 满足判定 WebViewJavascriptBridgeURL,将会进入核心代码区域接着会被判定为 BridgeLoadedURL 执行注入 JS 代码的方法,即 [_base injectJavascriptFile];

这样就形成了初始化桥,与android中的将所有 JsBridge 函数调用放入 window.WVJBCallbacks 数组中,后面要处理的逻辑一样了。

- (void)injectJavascriptFile {// 获取到 WebViewJavascriptBridge_JS 的代码NSString *js = WebViewJavascriptBridge_js();// 将获取到的 js 通过代理方法注入到当前绑定的 WebView 组件[self _evaluateJavascript:js];// 如果当前已有消息队列则遍历并分发消息,之后清空消息队列if (self.startupMessageQueue) {NSArray* queue = self.startupMessageQueue;self.startupMessageQueue = nil;for (id queuedMessage in queue) {[self _dispatchMessage:queuedMessage];}}
}

最后,调用 setupWebViewJavascriptBridge ,然后使用桥注册处理程序并调用 ObjC 处理程序:

setupWebViewJavascriptBridge(function(bridge) {/* Initialize your app here */bridge.registerHandler('JS to IOS', function(data, responseCallback) {console.log("IOS received response:", data)responseCallback(data)})bridge.callHandler('IOS to JS', {'key':'value'}, function responseCallback(responseData) {console.log("JS received response:", responseData)})
})

最后总结一下WebViewJavascriptBridge 的工作流:

  • JS 端加入 src 为 https://__bridge_loaded__ 的 iframe
  • Native 端检测到 Request,检测如果是 __bridge_loaded__ 则通过当前的 WebView 组件注入 WebViewJavascriptBridge_JS 代码
  • 注入代码成功之后会加入一个 messagingIframe,其 src 为 https://__wvjb_queue_message__
  • 之后不论是 Native 端还是 JS 端都可以通过 registerHandler 方法注册一个两端约定好的 HandlerName 的处理,也都可以通过 callHandler 方法通过约定好的 HandlerName 调用另一端的处理(两端处理消息的实现逻辑对称)

看到这里,其实Android和IOS系统最大的不同,相信大家已经感受到了,那就处理流程顺序不太一样。

但其实在公司的项目里面,上面这些知识只是基础,而公司内,都会制定一套完整的sdk来,各个端对齐,而sdk中,也不是简单的通信,还会有各种兼容,比如当前运行的系统,是否在指定的app内,app的版本信息,各个通信函数是否支持最低的版本号…

如果需要封装自己项目中的webview组件,还需要另外实现HTTP cookie注入,自定义User-Agent,白名单或者权限校验等等功能,更进一步还需要对webview组件进行初始化速度,页面渲染速度以及页面缓存策略的优化。

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

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

相关文章

Go实现在线词典翻译(三种翻译接口,结合sync)

火山翻译 首先介绍用火山翻译英译汉。 package mainimport ("bufio""bytes""encoding/json""fmt""io""log""net/http""os""strings""unicode" )type DictRequestHS st…

单片机第一季:零基础6——按键

目录 1,独立按键 2,矩阵按键 (注意:文章中的代码仅供参考学习,实际使用时要根据需要修改) 1,独立按键 按键管脚两端距离长的表示默认是导通状态,距离短的默认是断开状态&#xf…

Web APIs

文章目录 1.Web APIs 和 JS 基础关联性1.1 JS 的组成 2. API 和 Web API2.1 API2.2 Web API 1.Web APIs 和 JS 基础关联性 1.1 JS 的组成 2. API 和 Web API 2.1 API **API(Application Programming Interface,应用程序编程接口)**是一些预先定义的函…

观察者模式(下):如何实现一个异步非阻塞的EventBus框架?

上一节课中,我们学习了观察者模式的原理、实现、应用场景,重点介绍了不同应用场景下,几种不同的实现方式,包括:同步阻塞、异步非阻塞、进程内、进程间的实现方式。 同步阻塞是最经典的实现方式,主要是为了…

SSH框架简介篇

文章目录 概述目录结构 strutsSpringHibernate总结 概述 SSH框架(Struts Spring Hibernate)是一种广泛应用的Java企业级开发框架组合,它将Struts、Spring和Hibernate三个优秀的框架有机地结合在一起,提供了一套完整的解决方案&…

Linux系统编程:文件系统和inode

目录 一. 磁盘的结构和读写数据的方式 1.1 磁盘级文件和内存级文件 1.2 磁盘的物理结构 1.3 访问磁盘数据的方式 二. 磁盘文件系统 2.1 磁盘的分区管理方法 2.2 文件名和inode的关系 三. 结合文件系统对文件创建和删除的相关问题的理解 3.1 文件创建时操作系统进行的工…

51单片机--DS1302时钟

文章目录 DS1302引脚定义和应用电路内部结构框图寄存器的定义时序定义BCD码DS1302时钟代码 DS1302 DS1302是美国DALLAS公司推出的一款实时时钟电路芯片。它具有高性能和低功耗的特点,可以通过SPI三线接口与CPU进行同步通信。DS1302能够提供秒、分、时、日、星期、月…

【SQL应知应会】表分区(一)• MySQL版

欢迎来到爱书不爱输的程序猿的博客, 本博客致力于知识分享,与更多的人进行学习交流 本文收录于SQL应知应会专栏,本专栏主要用于记录对于数据库的一些学习,有基础也有进阶,有MySQL也有Oracle 分区表 • MySQL版 一、分区表1.非分区表2.分区表2…

利用集合框架实现-超市会员管理系统

借助集合框架来实现超市会员管理系统,实现以下功能: 1.开卡 2.积分累计 3.查询剩余积分 4.积分兑换 5.修改密码 6.退出 -------------------------------------------------------------------------------------------------- 展示&#x…

【信号去噪和分类】基于小波的隐马尔可夫模型统计信号处理(Matlab代码实现)

💥💥💞💞欢迎来到本博客❤️❤️💥💥 🏆博主优势:🌞🌞🌞博客内容尽量做到思维缜密,逻辑清晰,为了方便读者。 ⛳️座右铭&a…

C语言实现扫雷【经典】

前言   本篇文章要实现的是扫雷游戏,其代码实现与上一篇的三子棋游戏类同,都是在棋盘的基础上,与电脑进行对抗,不同的是,扫雷游戏一开始电脑就已经随机布置好了所有“雷”。 请戳 --->三子棋 扫雷游戏 1. 扫雷游…

MySQL每日一练——MySQL多表查询进阶挑战

目录 1、首先创建表 t_dept: t_emp: 2、插入数据 t_dept表: t_tmp表: 3、修改表 4、按条件查找 1、首先创建表 t_dept: CREATE TABLE t_dept (id INT(11) NOT NULL AUTO_INCREMENT,deptName VARCHAR(30) DEFAULT NULL,address VARCHAR(40) DEFAULT NULL,P…

Python结巴中文分词笔记

📚 jieba库基本介绍 🌐 jieba库概述 Jieba是一个流行的中文分词库,它能够将中文文本切分成词语,并对每个词语进行词性标注。中文分词是自然语言处理的重要步骤之一,它对于文本挖掘、信息检索、情感分析等任务具有重要…

Linux服务器丢包故障的解决思路及引申的TCP/IP协议栈理论

Linux服务器丢包故障的解决思路及引申的TCP/IP协议栈理论 我们使用Linux作为服务器操作系统时,为了达到高并发处理能力,充分利用机器性能,经常会进行一些内核参数的调整优化,但不合理的调整常常也会引起意想不到的其他问题&#x…

Elasticsearch原理剖析

一、 Elasticsearch结构 Elasticsearch集群方案由EsMaster、EsClient和EsNode1、EsNode2、EsNode3、EsNode4、EsNode5、EsNode6、EsNode7、EsNode8、EsNode9进程组成,如下图所示,模块说明如表下所示。 说明如表: 名称说明ClientClient使用H…

Android系统启动流程分析

当按下Android系统的开机电源按键时候,硬件会触发引导芯片,执行预定义的代码,然后加载引导程序(BootLoader)到RAM,Bootloader是Android系统起来前第一个程序,主要用来拉起Android系统程序,Android系统被拉起…

C# Linq 详解四

目录 概述 二十、SelectMany 二十一、Aggregate 二十二、DistinctBy 二十三、Reverse 二十四、SequenceEqual 二十五、Zip 二十六、SkipWhile 二十七、TakeWhile C# Linq 详解一 1.Where 2.Select 3.GroupBy 4.First / FirstOrDefault 5.Last / LastOrDefault C# Li…

排序子序列,倒置字符串讲解(图文并茂)

目录 1.排序子序列 2.倒置字符串 1.排序子序列 排序子序列_牛客笔试题_牛客网 (nowcoder.com) 首先题干中提到非递增序列和非递减序列,那么我们就要先弄明白什么是上述2种序列: 非递增序列:a[i] > a[i1] 如:3 2 1 或者 3 3 …

使用docker简单创建一个python容器

/root/docker_python目录结构: . |-- demo | -- main.py -- docker-compose.ymlmain.py内容: # codingutf-8 # -*- coding: utf-8 -*-if __name__ __main__:print("hello world")docker-compose.yml内容: version: "3&q…

注册中心技术Eureka、Nacos

说明:在微服务框架中,各个服务之间都是独立的。理论上来说,各个服务之间是可以直接通信的,但实际上因为服务之间通信需要管理和规划,如请求怎么负载均衡、请求怎么降级处理等等,所以就需要使用一个技术&…