WebViewJavascriptBridge
Web 页面中的 JS 与 iOS Native 如何交互?JS 和 iOS Native 就好比两块没有交集的大陆,如果想要使它们相互通信就必须要建立一座“桥梁”。
WebViewJavascriptBridge 是盛名已久的 JSBridge 库,它仅使用了少量代码就实现了对于 Mac OS X 的 WebView 以及 iOS 平台的 UIWebView 和 WKWebView 三种组件的完美支持。
WebViewJavascriptBridge 主要是作为 Mac OS X 和 iOS 端(Native 端)与 JS 端相互通信,互相调用的桥梁。对于 Mac OS X 和 iOS 两种平台包含的三种 WebView 功能组件而言,WebViewJavascriptBridge 做了隐性适配,即仅用一套代码即可绑定不同平台的 WebView 组件实现同样功能的 JS 通信功能。 WebViewJavascriptBridge 对于 JS 端和 Native 端设计了对等的接口,不论是 JS 端还是 Native 端,注册本端的响应处理都是用 registerHandler
接口,调用另一端(给另一端发消息)都是用 callHandler
接口。
- UIWebView 使用 javaScriptCore.
- WKWebView 使用 WKUserContentController.
UIWebView 原生的交互原理
通过一个 JSContext 获取 UIWebView 的 JS 执行上下文。
然后通过这个上下文,进行 OC & JS 的双端交互。
_jsContext = [webView valueForKeyPath:@"documentView.webView.mainFrame.javaScriptContext"];_jsContext.exceptionHandler = ^(JSContext *context, JSValue *exception) {NSLog(@"%@",@"获取 WebView JS 执行环境失败了!");};
WKWebView 原生交互原理
通过 userContentController 把需要观察的 JS 执行函数注册起来。
然后通过一个协议方法,将所有注册过的 JS 函数执行的参数传递到此协议方法中。
注册 需要 观察的 JS 执行函数
[webView.configuration.userContentController addScriptMessageHandler:self name:@"jsFunc"];
在 JS 中调用这个函数并传递参数数据
window.webkit.messageHandlers.jsFunc.postMessage({name : "李四",age : 22});
OC 中遵守 WKScriptMessageHandler
协议。
- (void)userContentController:(WKUserContentController *)userContentController didReceiveScriptMessage:(WKScriptMessage *)message
此协议方法里的 WKScriptMessage 有 name & body 两个属性。 name 可以用来判断是哪个 JSFunc 调用了。body 则是 JSFunc 传递到 OC 的参数。
WebViewJavaScriptBridge
WebViewJavaScriptBridge 用于 WKWebView & UIWebView 中 OC 和 JS 交互。
它的基本原理是:
把 OC 的方法注册到桥梁中,让 JS 去调用。
把 JS 的方法注册在桥梁中,让 OC 去调用。
WebViewJavaScriptBridge 使用的基本步骤
- 首先在项目中导入
WebViewJavaScriptBridge 框架
。
pod ‘WebViewJavascriptBridge’
- 导入头文件
#import <WebViewJavascriptBridge.h>
。 - 建立 WebViewJavaScriptBridge 和 WebView 之间的关系。
_jsBridge = [WebViewJavascriptBridge bridgeForWebView:_webView];
- 在HTML 文件中,复制粘贴这两段 JS 函数。
function setupWebViewJavascriptBridge(callback) {if (window.WebViewJavascriptBridge) {return callback(window.WebViewJavascriptBridge)}if (window.WVJBCallbacks) {return window.WVJBCallbacks.push(callback)}window.WVJBCallbacks = [callback] // 创建一个 WVJBCallbacks 全局属性数组,并将 callback 插入到数组中。var WVJBIframe = document.createElement('iframe') // 创建一个 iframe 元素WVJBIframe.style.display = 'none' // 不显示WVJBIframe.src = 'https://__bridge_loaded__' // 设置 iframe 的 src 属性document.documentElement.appendChild(WVJBIframe) // 把 iframe 添加到当前文导航上。setTimeout(function() {document.documentElement.removeChild(WVJBIframe)}, 0)}// 这里主要是注册 OC 将要调用的 JS 方法。setupWebViewJavascriptBridge(function(bridge){});
到此为止,基本的准备工作就做完了。现在需要往桥梁中注入 OC 方法 和 JS 函数了。
往桥梁中注入 OC 方法 和 JS 函数
1、往桥梁中注入 OC 方法。
[_jsBridge registerHandler:@"scanClick" handler:^(id data, WVJBResponseCallback responseCallback) {NSLog(@"dataFrom JS : %@",data[@"data"]);responseCallback(@"扫描结果 : www.baidu.com");}];
这段代码的意思:
- scanClick 是 OC block 的一个别名。
- block 本身,是 JS 通过某种方式调用到 scanClick 的时候,执行的代码块。
- data ,由于 OC 这端由 JS 调用,所以 data 是 JS 端传递过来的数据。
- responseCallback OC 端的 block 执行完毕之后,往 JS 端传递的数据。
2、往桥梁中注入 JS 函数.
在 JS 的方法如何注入到桥梁呢?需要在第二段 JS 代码中,注入 JS 的函数。
// 这里主要是注册 OC 将要调用的 JS 方法。setupWebViewJavascriptBridge(function(bridge){// 声明 OC 需要调用的 JS 方法。bridge.registerHanlder('testJavaScriptFunction',function(data,responseCallback){// data 是 OC 传递过来的数据.// responseCallback 是 JS 调用完毕之后传递给 OC 的数据alert("JS 被 OC 调用了.");responseCallback({data: "js 的数据",from : "JS"});})});
这段代码的意思:
- testJavaScriptFunction 是注入到桥梁中 JS 函数的别名。以供 OC 端调用。
- 回调函数的 data。 既然 JS 函数由 OC 调用,所以 data 是 OC 端传递过来的数据。
- responseCallback 。 JS 调用在被 OC 调用完毕之后,向 OC 端传递的数据。
基本就是:
OC 端注册 OC 的方法,OC 端调用 JS 的函数。
JS 端注册 JS 的函数,JS 端调用 OC 的方法。
场景
JS -> OC 的交互
在 HTML 中,有个按钮,点击这个按钮,修改 NavigationBar 的颜色。
- 在 OC 端,往桥梁注入一个修改 NavigationBar 颜色的 block.
- 在 JS 端,调用这个 block,来间接的达到修改颜色的目的。
首先,在 OC 中,通过 WebViewJavascriptBridge 注册一个修改 navigationBar 颜色的 Block。
[_jsBridge registerHandler:@"colorClick" handler:^(id data, WVJBResponseCallback responseCallback) {self.navigationController.navigationBar.barTintColor = [UIColor colorWithRed:arc4random_uniform(256) / 255.0 green:arc4random_uniform(256) / 255.0 blue:arc4random_uniform(256) / 255.0 alpha:1.0];responseCallback(@"颜色修改完毕!");}];
然后再 JS 中,通过某种方式去调用这个 OC 的 block。
WebViewJavascriptBridge.callHandler('colorClick',function(dataFromOC) {alert("JS 调用了 OC 注册的 colorClick 方法");document.getElementById("returnValue").value = dataFromOC;})
这里通过某种方式就是使用 WebViewJavascriptBridge.callHandler('OC 中block 别名',callback)的方式来调用。
OC -> JS 的交互
OC 上有一个UIButton,点击这儿按钮,把 HTML body 的颜色修改成橙色。
首先,往桥梁中,注入一个修改 HTML body 颜色的 JSFunction。
// 在这里声明 OC 需要主动调用 JS 的方法。setupWebViewJavascriptBridge(function(bridge) {bridge.registerHandler('changeBGColor',function(data,responseCallback){// alert('aaaaaa');document.body.style.backgroundColor = "orange";document.getElementById("returnValue").value = data;});});
然后在 OC 端通过桥梁调用这个 changeBGColor
。
[_jsBridge callHandler:@"changeBGColor" data:@"把 HTML 的背景颜色改成橙色!!!!"];
执行效果:
补充
OC 调用 JS 的三种情况。
// 单纯的调用 JSFunction,不往 JS 传递参数,也不需要 JSFunction 的返回值。[_jsBridge callHandler:@"changeBGColor"];// 调用 JSFunction,并向 JS 传递参数,但不需要 JSFunciton 的返回值。[_jsBridge callHandler:@"changeBGColor" data:@"把 HTML 的背景颜色改成橙色!!!!"];// 调用 JSFunction ,并向 JS 传递参数,也需要 JSFunction 的返回值。[_jsBridge callHandler:@"changeBGColor" data:@"传递给 JS 的参数" responseCallback:^(id responseData) {NSLog(@"JS 的返回值: %@",responseData);}];
JS 调用 OC 的三种情况。
// JS 单纯的调用 OC 的 block
WebViewJavascriptBridge.callHandler('scanClick');// JS 调用 OC 的 block,并传递 JS 参数
WebViewJavascriptBridge.callHandler('scanClick',"JS 参数");// JS 调用 OC 的 block,传递 JS 参数,并接受 OC 的返回值。
WebViewJavascriptBridge.callHandler('scanClick',{data : "这是 JS 传递到 OC 的扫描数据"},function(dataFromOC){alert("JS 调用了 OC 的扫描方法!");document.getElementById("returnValue").value = dataFromOC;});
可以根据实际情况,选择合适的方法。
关于在 OC 中,往桥梁中注入 block 的注意点。
在当前控制器消失的时候,要记得把注入到桥梁中的 OC block,从桥梁中删除。
否则,可能会出现控制器无法释放的情况。
- (void)viewDidDisappear:(BOOL)animated {[super viewDidDisappear:animated];[_jsBridge removeHandler:@"scanClick"];[_jsBridge removeHandler:@"colorClick"];[_jsBridge removeHandler:@"locationClick"];[_jsBridge removeHandler:@"shareClick"];[_jsBridge removeHandler:@"payClick"];[_jsBridge removeHandler:@"goBackClick"];
}
Android
以上说的都是ios交互,安卓有一点点不同。
function connectWebViewJavascriptBridge (callback) { if (window.WebViewJavascriptBridge) {callback(WebViewJavascriptBridge)} else {document.addEventListener('WebViewJavascriptBridgeReady', function() {callback(WebViewJavascriptBridge)},false);}
}//和ios一样
connectWebViewJavascriptBridge (function(bridge) {//注册一个方法(方法名是“JS Echo”),客户端进行调用(方法名也是“JS Echo”),responseCallback是回调函数bridge.registerHandler('JS Echo', function(data, responseCallback) {console.log("JS Echo called with:", data)responseCallback(data)})//客户端已经注册好一个名为“ObjC Echo”的方法,H5直接进行调用(方法名也为“ObjC Echo”)就行,调用的时候可以传客户端需要的参数bridge.callHandler('ObjC Echo', {'key':'value'}, function responseCallback(responseData) {console.log("JS received response:", responseData)})
})
封装:
/* eslint-disable */function setAndroid () {var bridge = {default: this,callHandler: function(b, a, c) {var e = '''function' == typeof a && ((c = a), (a = {}))a = { data: void 0 === a ? null : a }if ('function' == typeof c) {var g = 'dscb' + window.dscb++window[g] = ca._dscbstub = g}a = JSON.stringify(a)if (window._dsbridge) e = _dsbridge.callHandler(b, a)else if (window._dswk ||-1 != navigator.userAgent.indexOf('_dsbridge'))e = prompt('_dsbridge=' + b, a)return JSON.parse(e || '{}').data},register: function(b, a, c) {c = c ? window._dsaf : window._dsfwindow._dsInit ||((window._dsInit = !0),setTimeout(function() {bridge.callHandler('_dsb.dsinit')}, 0))'object' == typeof a ? (c._obs[b] = a) : (c[b] = a)},registerAsyn: function(b, a) {this.register(b, a, !0)},hasNativeMethod: function(b, a) {return this.callHandler('_dsb.hasNativeMethod', {name: b,type: a || 'all'})},disableJavascriptDialogBlock: function(b) {this.call('_dsb.disableJavascriptDialogBlock', {disable: !1 !== b})}}!(function() {if (!window._dsf) {var b = {_dsf: { _obs: {} },_dsaf: { _obs: {} },dscb: 0,WebViewJavascriptBridge: bridge,close: function() {bridge.callHandler('_dsb.closePage')},_handleMessageFromNative: function(a) {var e = JSON.parse(a.data),b = { id: a.callbackId, complete: !0 },c = this._dsf[a.method],d = this._dsaf[a.method],h = function(a, c) {b.data = a.apply(c, e)bridge.call('_dsb.returnValue', b)},k = function(a, c) {e.push(function(a, c) {b.data = ab.complete = !1 !== cbridge.callHandler('_dsb.returnValue', b)})a.apply(c, e)}if (c) h(c, this._dsf)else if (d) k(d, this._dsaf)else if (((c = a.method.split('.')), !(2 > c.length))) {a = c.pop()var c = c.join('.'),d = this._dsf._obs,d = d[c] || {},f = d[a]f && 'function' == typeof f? h(f, d): ((d = this._dsaf._obs),(d = d[c] || {}),(f = d[a]) &&'function' == typeof f &&k(f, d))}}},afor (a in b) window[a] = b[a]bridge.register('_hasJavascriptMethod', function(a, b) {b = a.split('.')if (2 > b.length) return !(!_dsf[b] && !_dsaf[b])a = b.pop()b = b.join('.')return (b = _dsf._obs[b] || _dsaf._obs[b]) && !!b[a]})}})();return bridge
}const jsBridge = {init: function() {if (/(Android)/i.test(navigator.userAgent.toLowerCase())) {// setAndroid()this.register = setAndroid().register}},connectJsBridge: function(callback) {if (window.WebViewJavascriptBridge) {return callback(window.WebViewJavascriptBridge)}if (window.WVJBCallbacks) {return window.WVJBCallbacks.push(callback)}window.WVJBCallbacks = [callback]var WVJBIframe = document.createElement('iframe')WVJBIframe.style.display = 'none'WVJBIframe.src = 'https://__bridge_loaded__'document.documentElement.appendChild(WVJBIframe)setTimeout(function() {document.documentElement.removeChild(WVJBIframe)}, 0)},addBridgeProperty: function(bridge) {const dependencies = ['getUserInfo', 'login', 'getLocation', 'share',..... ];try {dependencies.forEach(dependency => {// if (!this[dependency]) {Object.defineProperty(this, dependency, {configurable: true, // 注:允许重复定义属性(移除会造成重新定义报错)get: () => {return bridge.callHandler.bind(bridge, dependency)}})// }})} catch (error) {console.error(error)}// if (this.openWebPage) {this.openWebPage = url => {window.location.href = `app内嵌的域名?url=${encodeURIComponent(url)}`}// }return this},ready: function(callback) {this.connectJsBridge(bridge => {callback(this.addBridgeProperty(bridge))})},register: function(...args) {window.WebViewJavascriptBridge.registerHandler(...args)},
}jsBridge.init()
export default jsBridge//使用:
import JSBridge from './base/jsBridge'JSBridge.ready((bridge: any) => {bridge.getUserInfo((json: string | object) => {console.log(json)})
})
WebViewJavascriptBridge相关推荐
- WebViewJavascriptBridge的简单使用
- (void)viewDidLoad { UIWebView* webView = [[UIWebView alloc] initWithFrame:self.view.bounds]; webVi ...
- iOS WebviewJavascriptBridge 源码研读笔记
这两天接近元旦,事情稍微少些,有些时间,索性写点什么,就从最擅长的iOS混合开发写起了,由于iOS开发经验不到四年吧,期间还搞了一年半的前端,有些知识可能还是积累的不足,能力不足,水平有限,可能有谬误 ...
- iOS原生与html交互 使用第三方WebViewJavascriptBridge
HTML页面代码 <!DOCTYPE html> <html xmlns:http="http://www.w3.org/1999/xhtml"> < ...
- iOS下JS与OC互相调用(五)--UIWebView + WebViewJavascriptBridge
WebViewJavascriptBridge是一个有点年代的JS与OC交互的库,使用该库的著名应用还挺多的,目前这个库有7000+star.我去翻看了它的第一版本已经是4年前了,在版本V4.1.4以 ...
- WebViewJavascriptBridge原理解析
基本说明 我们的项目是一个OC与javascript重度交互的app,OC与javascript交互的那部分是在WebViewJavascriptBridge的github地址的基础上修改的,WebV ...
- 通过WebViewJavascriptBridge实现OC与JS交互
这里照搬Github的Demo,其实还是很易懂的,首先,要在控制器的.h文件当中实现浏览器控件的协议: 1 #import <UIKit/UIKit.h> 2 3 @interface E ...
- web与APP之间的交互---WebViewJavascriptBridge
在实际项目之中,经常会遇到app之中嵌入网页的情况(Hybrid),就需要web网页与原生app之间交互,比如获取当前用户信息等.一种简单的方式就是通过url参数来搞定,但是这种方式异常死板,所以有了 ...
- (0005) iOS 开发之WebViewJavascriptBridge的升级问题
注意:这里讲的是升级:不会用的先去学习怎么使用. 为什么我想起来升级尼?(使用的4.1.4-5.0.5) 问题:使用的WebViewJavascriptBridge4.1.4的使用,iOS 10上面释 ...
- iPhone 和 iPad的ios 开发中 利用 WebViewJavascriptBridge组件,通过 UIWebView 对Html进行双向通讯...
本文转载至 http://blog.csdn.net/remote_roamer/article/details/7261490 WebViewJavascriptBridge 项目的 官网 http ...
- iOS下JS与OC互相调用(六)--WKWebView + WebViewJavascriptBridge
2019独角兽企业重金招聘Python工程师标准>>> iOS下JS与OC互相调用(六)--WKWebView + WebViewJavascriptBridge 转载:原地址 ht ...
最新文章
- html怎么把图片作为背景_抖音背景图片怎么弄,抖音背景图片引导关注
- centos7全离线安装redis3.2.8集群
- pytest使用简介
- 皮一皮:同一样物件,不一样的时间...
- Java四大知识点讲解,初学者必看
- cat全链路监控_谛听全链路监控平台实践与思考
- python对话机器人框架_长篇文讲解:使用Python AIML搭建聊天机器人的方法示例(收藏)...
- 谷歌不支持调用摄像头麦克风_谷歌发布安卓11系统:全新界面、更严的隐私管理...
- 用ExtJs+Linq+Wcf打造简单grid
- 进程占用过高cpu的排查
- 技术和技术管理人员评价标准
- linux档案内容怎么写,Linux cat输出档案命令详解
- unityui计分_铅计分成长
- Mac Air 配置Android开发环境
- RS232串口线连接方法
- sap销售发货的流程_现金及快速销售流程
- [MySQL远程备份策略举例]
- Bootstrap-03 (前台开发框架)
- GPRS网络组成及接口
- 从优步僵尸车事件看源码保护的必要性