项目上线也有一段时间了,新项目大量使用了H5页面,因此存在很多需要跟Native进行数据交互的需求。起初,我们项目开发沿用了标准版的Native与H5的交互框架Cordova插件,但是在开发接近尾声时考虑到Cordova本身的特点,虽然功能强大,但是很多并不适合与我们的项目,以及一些缺陷,因此提出更换JS与Native通信的插件。
团队对目前主流的使用框架做了调研,相关调研结果如下:
框架 | 兼容性 | 易用性(1最易) | 社区热度(3最热) | 可维护性(1最易) | 参考文档(3最丰富) | 调用H5标准 |
---|---|---|---|---|---|---|
JSBridge | iOS/Android | 1 | 1 | 2 | 2 | 是 |
WebViewJavaScriptBridge | iOS/Android | 1 | 3 | 1 | 3 | 是 |
Cordova | iOS/Android | 3 | 1 | 3 | 1 | 是 |
使用上的优缺点对比:
框架 | 优点 | 缺点 |
---|---|---|
JSBridge | H5页面无需加载额外文件,无需声明,直接调用即可 | 安卓实现桥接的方式可能跟现有业务有冲突,代码相对比较多,长时间没有维护 |
WebViewJavaScriptBridge | 源码简单易懂,后期可自行维护H5页面无需加载额外文件,需声明,声明后,直接调用即可 | 安卓没有持续维护,代码比较陈旧,需要自己去优化代码 |
Cordova | 功能非常强大 | H5页面需加载多个js文件;每增加一个插件需要配置,较麻烦 |
从易用性,社区热度,可维护性方面考虑:团队考虑选用WebViewJavaScriptBridge作为汇付项目的H5与Native通信框架。
WebViewJavaScriptBridge到底能干什么?
Native发布新功能新活动,很多情况下是通过发布新版本去实现的。然而对于在app内需要经常更新的页面,很多情况下我们会使用h5页面去实现,可以做到数据更新更加的及时。那么问题来了?
对于iOS而言,JS与OC 之间本身是不存在任何交互通信的,也就是说,JS不能够直接调用Native实现的方法。利用WebViewJavaScriptBridge 可以很好的实现JS和OC之间的相互调用,这就是WebViewJavaScriptBridge要做的事情。
实现JS与OC之间通信的需要借助的是iOS webView代理方法:-(BOOL)webView:(UIWebView *)webView shouldStartLoadWithRequest:(NSURLRequest *)request navigationType:(UIWebViewNavigationType)navigationType 该方法在每次做重定向的时候都会被调用到,所以可以通过拦截做一下坏事。
WebViewJavaScriptBridge 实现JS调用Native大致流程:
![](https://img.haomeiwen.com/i3908325/34d44dae0cc2dd9c.png)
WebViewJavaScriptBridge 实现Native调用JS大致流程:
![](https://img.haomeiwen.com/i3908325/ee8586fa082fe69e.png)
核心语法介绍:
1.Native调用JS实使用的原生方法是:[_webView stringByEvaluatingJavaScriptFromString:@””];
2.-(BOOL)webView:(UIWebView *)webView shouldStartLoadWithRequest:(NSURLRequest *)request navigationType:(UIWebViewNavigationType)navigationType iOS 原生webview的代理方法,每次重定向时都会被调用
3.- (void)registerHandler:(NSString *)handlerName handler:(WVJBHandler)handler Native注册与H5交互的指定方法
4.- (void)callHandler:(NSString *)handlerName data:(id)data 实现Native调用JS方法,该方法是对
stringByEvaluatingJavaScriptFromString 做了一层封装。
遇到的坑:
1.Native 调用js会阻塞主线程,解决方法使用:[self.myWebView performSelectorOnMainThread:@selector(stringByEvaluatingJavaScriptFromString:) withObject:javascript waitUntilDone:NO]; 重要的是最后那个参数:设置为NO表示不等待selector执行完毕。
2.数据混淆。解决办法:使用native调用js的方法去做,因为初始化出来的callHandler肯定是唯一的。Example:在友盟SDK6.0以下版本的分享成功回调使用的时代理协议去实现的,这就意味着回调执行要在全局进行保存,这就有可能出现多个回调时数据出现混淆。
3.在数据传递方面上要使用对象的方式来传递数据,避免使用数组。数组的坑你们懂得啦..OC上取值为空,马上给你闪退。
初始化WebViewJavaScriptBridge:
WebViewJavascriptBridge *bridge = [WebViewJavascriptBridge bridgeForWebView:self.myWebView];
[bridge setWebViewDelegate:self];
self.myBridge = bridge;
WebViewJSBridgeHelper *helper = [[WebViewJSBridgeHelper alloc] initWithWebView:self.myWebView registerHandlersWithBridge:bridge sender:self completeBlock:^{
[self requestPage];
}];
[helper registerPlugin];
WebViewJSBridgeHelper 代码,封装到一个文件里面只是为了代码美观点,写到controller里面也可以实现功能。
- (void)registerPlugin {
[_jsBridge registerHandler:@"testObjcCallback" handler:^(id data, WVJBResponseCallback responseCallback) {
[TestViewController showWithSender:_viewController];
}];
[_jsBridge registerHandler:@"reloadPage" handler:^(id data, WVJBResponseCallback responseCallback) {
if (_completeBlock) {
_completeBlock();
}
}];
[_jsBridge registerHandler:@"changeBackground" handler:^(id data, WVJBResponseCallback responseCallback) {
NSDictionary *jsonDict = (NSDictionary *)data;
NSString *colorName = [jsonDict objectForKey:@"key"];
if ([colorName isEqualToString:@"red"]) {
_viewController.view.backgroundColor = [UIColor redColor];
} else if([colorName isEqualToString:@"blue"]) {
_viewController.view.backgroundColor = [UIColor blueColor];
} else if ([colorName isEqualToString:@"green"]) {
_viewController.view.backgroundColor = [UIColor greenColor];
} else if ([colorName isEqualToString:@"yellow"]) {
_viewController.view.backgroundColor = [UIColor yellowColor];
}
}];
}
Native 调用JS实例:
Native 上UI界面上随便写个iOS 按钮就行了。
- (void)testCallBack:(id)sender {
[_myBridge callHandler:@"testJavascriptHandler" data:@{@"key":@"name",@"value":@"Jakin"} responseCallback:^(id responseData) {
}];
}
前端代码js文件代码:
window.onerror = function(err) {}
function setupWebViewJavascriptBridge(callback) {
if (window.WebViewJavascriptBridge) {
return callback(WebViewJavascriptBridge);
}
if (window.WVJBCallbacks) {
return window.WVJBCallbacks.push(callback);
}
window.WVJBCallbacks = [callback];
var WVJBIframe = document.createElement('iframe');
WVJBIframe.style.display = 'none';
WVJBIframe.src = 'wvjbscheme://__BRIDGE_LOADED__';
document.documentElement.appendChild(WVJBIframe);
setTimeout(function() {
document.documentElement.removeChild(WVJBIframe) }, 0)
}
setupWebViewJavascriptBridge(function(bridge) {
bridge.registerHandler('testJavascriptHandler', function(data, responseCallback) {
var message = JSON.stringify(data);
var div = document.getElementById('testMessage');
var pElement = document.createElement('p');
pElement.innerText = message;
div.appendChild(pElement);
var responseData = { 'Javascript Says':'Right back atcha!' } responseCallback(responseData); })
var reloadPageButton = document.getElementById('reloadPage');
reloadPageButton.onclick = (function (e) {
e.preventDefault();
bridge.callHandler('reloadPage', {'foo': 'bar'}, function(response) { }) })
var callbackButton = document.getElementById('gotoPage');
callbackButton.onclick = function(e) {
e.preventDefault() ;
bridge.callHandler('testObjcCallback', {'foo': 'bar'}, function(response) { }) }
var element = document.getElementById('selectType');
element.onchange = function (e) {
e.preventDefault();
var SId = element.options[element.selectedIndex].value;
bridge.callHandler('changeBackground', {'key': SId},function(response) {
})
}})
hbs文件代码,前端代码写的也比较粗糙。这里页面是使用handlebars后端渲染出来,具体使用不介绍了。
<h1> {{title}}</h1>
<button id="gotoPage" style="width: 100px; height: 50px; background-color: #00B7FF; margin-left: 40px;"> 跳转页面</button>
<button id="reloadPage" style="width: 100px; height: 50px; background-color: #880000; margin-right: 40px;float: right"> 刷新webview</button><div style="margin-top: 10px">
<select style="width: 100px" id="selectType">
<option value="blue">蓝色</option>
<option value="green">绿色</option>
<option value="red">红色</option>
<option value="yellow">黄色</option>
</select>
</div><div id="testMessage">
</div><script src="javascripts/webViewJSBridgeDemo.js"></script>
网友评论