美文网首页iOS备忘录
WebViewJavaScriptBridge入门与项目实战

WebViewJavaScriptBridge入门与项目实战

作者: 刘永勇 | 来源:发表于2016-12-04 17:08 被阅读0次

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

    Paste_Image.png

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

    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后端渲染出来,具体使用不介绍了。

    <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>
    

    相关文章

      网友评论

        本文标题:WebViewJavaScriptBridge入门与项目实战

        本文链接:https://www.haomeiwen.com/subject/psiqmttx.html