专业网站制作企业,wordpress4.5.2文章采集,做网站的软件项目进度计划,二维码图片制作本文讲述h5页面跟原生通信#xff0c;比如在app内#xff0c;调用相机#xff0c;获取相册内的图片#xff0c;在app内拉起微信小程序等等#xff0c;h5页面没有这么多权限能够直接调用移动端的原生能力#xff0c;这个时候就需要与原生进行通讯#xff0c;传递一个信号…本文讲述h5页面跟原生通信比如在app内调用相机获取相册内的图片在app内拉起微信小程序等等h5页面没有这么多权限能够直接调用移动端的原生能力这个时候就需要与原生进行通讯传递一个信号给原生这边然后原生直接调用手机端的能力。
下面分别讲解h5与Androidios系统通信
JS与Android
Android处理
其实h5能够在app内中存在就是在app中有webview的存在在webview中可以是一个h5的链接这样这个h5页面就能够app内展现。
Android中往webview里面注入WebViewJavascriptBridge.js 我们能够看到在android往webview注入了一段js代码接下来我们看看这段代码的逻辑。 这里往webview中的document注入事件webViewJavascriptBridgeReady向window中添加一个对象WebViewJavascriptBridge。
注意这个WebViewJavascriptBridge对象中包含了initsendregisterHandlercallHandler_handleMessageFormNative函数。
接下来看这个init函数中的逻辑 使用 iframe 创建消息队列的主要原因是:
实现跨域通信。iframe 可以被嵌入到不同域的页面中,使得父子页面可以跨域通信。解耦通信方。使用 iframe,可以使得父页面和子页面的 JavaScript 环境相互隔离。父页面无法直接访问子页面的变量和函数,只能通过消息通信。这减少了两端的依赖,有利于后期维护。兼容性好。iframe 比较老的技术,广泛支持各种浏览器,不需要担心兼容性问题。简单易用。向 iframe 发送消息和监听消息非常简单,不需要额外的库或工具。 这里可以发现初始化就完成了创建消息队列初始化默认消息队列还有最重要的发送消息。
接下来就看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 WebViewJavascriptBridge 是用于在 WKWebViewUIWebView 和 WebView 中的 Obj-C 和 JavaScript 之间发送消息的 iOS / OSX 桥接器。
这里本质跟Android是一样的也是注入了一段js代码然后注入到webview组件中实现原生与js的交互。 我们先看看js处理的逻辑 这里跟Android还是很像往window中注入一个通讯桥通讯桥的名称是WebViewJavascriptBridge然后在通信桥上有registerHandlercallHandlerdisableJavscriptAlertBoxSafetyTimeout_fetchQueue_handleMessageFromObjC等方法。
接下来看一下如何往webview中注入的 与上面的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__ 的 iframeNative 端检测到 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组件进行初始化速度页面渲染速度以及页面缓存策略的优化。