WebViewJavaScriptBridge入门与项目实战

项目上线也有一段时间了,新项目大量使用了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大致流程:

WebViewJavaScriptBridge入门与项目实战_第1张图片
Paste_Image.png

WebViewJavaScriptBridge 实现Native调用JS大致流程:

WebViewJavaScriptBridge入门与项目实战_第2张图片
Paste_Image.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后端渲染出来,具体使用不介绍了。

{{title}}

你可能感兴趣的:(WebViewJavaScriptBridge入门与项目实战)