zoukankan      html  css  js  c++  java
  • webview和H5交互

    由于H5的灵活多变,动态可配的特点,也为了避免冗长 的审核周期,H5页面在app上的重要性正日益突显。

    iOS应用于H5交互的控件主要是UIWebView及WKWebView

    WKWebView是14年随iOS8推出的,很好的解决了UIWebView加载速度慢,内存占用大的问题

    WebViewJavaScriptBridge是一款轻量级的框架,使用它结合wkwebview能十分方便的实现源生与H5的交互

      webviewJavaScrptBridge的基本使用

      1.初始化需bind视图  [WebViewJavaScriptBridge bridgeForWebView:]

      2.设置代理  [self.bridge setWebViewDelegate:self]

      3.注册方法  

      [self.bridge registerHandler:@"click" handler:^(id data, WVJBResponseCallback responseCallback) {

            NSDictionary *dic = (NSDictionary *)data;

            [weakSelf responseJSWithData:(NSDictionary *)dic];

         }];

      click是方法名,handler是H5发起调用后传回的回调,该闭包第一个H5页面传递过来的参数,第二个是callBack对象

      4.调用方法  

      [self.bridge callHandler:event data:data responseCallback:^(id responseData) {

            NSLog(@"responseData:%@",responseData);

         }];

      event是方法名,源生直接发起回调,data是传递的参数,callBack是h5收到事件后传回的回调

      webviewJavaScriptBridge的精髓就是方法3,4的交替使用,使得源生调h5,h5调源生变的异常简单,两者之间的链接仅仅靠一个方法名,收方register后,发起方callHandler就能实现一条消息的有效传递,具体实现细节可细究其源码  webviewJavaScriptBridge

      现在来探究下H5端如何使用bridge注册及发起事件

    /// 配置bridge
    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 = 'https://__bridge_loaded__';
            document.documentElement.appendChild(WVJBIframe);
            setTimeout(function() { document.documentElement.removeChild(WVJBIframe) }, 0)
        }
    
    /// callHandler    data为H5传递参数
    setupWebViewJavascriptBridge(function (bridge) {
                let data = {'title': title}
                bridge.callHandler('setTitle', data)
            })
    
    /// registerHandler    data为源生传递参数
    bridge.registerHandler("result", function (data, responseCallback) {
                result(data['opType'], data['code'], data['msg'])
            })
    

       可见在H5端使用bridge完成消息的收发页十分方便。webviewJavaScriptCoreBridge的配置,是在H5页面生成一个iframe节点,传递消息时插入这个节点,结束后移除这个节点,以此来实现源生到H5的一次消息传递。

    WKNavigationDelegate  使用wkwebview进行一次网络请求中的各种事件回调

    WKUIDelegate   主要用于处理H5中的alert弹窗事件

      -runJavaScriptAlertPanelWithMessage:

    UIWebView,尽管该视图已经被WKWebview取代,但市面上大多数应用框架仍然使用的是webview,并且其更接近底层,具有深究价值

    UIWebview通常是通过拦截request,根据指定url中的参数,来实现H5端事件的调用

    func webView(_ webView: UIWebView, shouldStartLoadWith request: URLRequest, navigationType: UIWebViewNavigationType) -> Bool
        {
            let  absoluteString = request.url!.absoluteString
    		if (!absoluteString.contain(prefix)) {
    			return true
    		}else {
                  // do something return false } }

    返回false则拦截请求,截取url中所带参数,根据需求做相应处理。通常为封装方法间的调用,会插入一段js代码。

    override func webViewDidStartLoad(_ webView: UIWebView) {
            super.webViewDidStartLoad(webView)
            
            /// 尽可能在较早的时间点插入该js代码
            writtenJSApi(webView: webView)
        }
    
    func writtenJSApi(webView:UIWebView) {
            if  let filePath:String = Bundle.main.path(forResource: "jsapi", ofType: "js"){
                if let jsStr = try? NSString(contentsOfFile: filePath, encoding: String.Encoding.utf8.rawValue){
                    webView.stringByEvaluatingJavaScript(from: jsStr as String)
                }
            }
        }
    

     js主要代码

    // 异步
     window.JSApi.asyncInvoke = function (apiname, pramaJsonString, callbackapi)
     {
     var apiPath = "https://asyncjsapi.com/" + encodeURIComponent(apiname + window.JSApi.pramaSplit + pramaJsonString + window.JSApi.pramaSplit + callbackapi);
     var iframe = document.createElement("iframe");
     iframe.setAttribute("src", apiPath);
     document.documentElement.appendChild(iframe);
     iframe.parentNode.removeChild(iframe);
     iframe = null;
     }
     
     // 同步
     window.JSApi.syncInvoke = function (apiname, pramaJsonString)
     {
     var apiPath = "https://syncjsapi.com/" + encodeURIComponent(apiname + window.JSApi.pramaSplit + pramaJsonString );
     var request = new XMLHttpRequest();
     if (request == null)
     {
     return {
     code: 1,
     errorMsg: "not support XMLHttpRequest",
     data: null
     };
     }
     
     var responseText = ""
     
     request.onreadystatechange = function ()
     {
     // alert("stateChange"+request.readyState+","+request.status)
     if (request.readyState == 4 && request.status == 200)
     {
     responseText = request.responseText;
     }
     else
     {
     responseText = ""
     }
     // alert(responseText)
     };
     // alert(apiname+"  2  request open    "+apiPath);
     request.open("GET", apiPath, false);
     request.send(null);
     
     console.log(responseText)
     
     return responseText
     }
     })();
    

     可以看出,异步方法依然是在页面创建iframe,先插入后删除的方式;同步方法是利用ajax,创建了一个http请求,并监听request的状态,当status为200时,返回信息给H5。同步方法的拦截是在urlProtocol里面

    class JsApiURLProtocol: URLProtocol, URLSessionDataDelegate, URLSessionTaskDelegate {
    
    	override class func canInit(with request: URLRequest) -> Bool {
    
    		if let strUrl:String = request.url?.absoluteString
            {
                if JsApiHelper.isSyncNativeApi(urlString: strUrl) {
                    return true
                }
            }else {
            	return false
            }
    
    	}
    
    	override func startLoading() {
            let strUrl:String! = request.url!.absoluteString
            if JsApiHelper.isSyncNativeApi(urlString: strUrl) {
                // 调用同步jsapi
                let result: NSString = JsApiHelper.syncInvoke(urlString: strUrl! as NSString) as NSString
                let resultData: NSData = result.data(using: String.Encoding.utf8.rawValue)! as NSData
                var headerFields: [NSObject : AnyObject]? = [NSObject : AnyObject]()
                headerFields?.updateValue("*" as AnyObject, forKey: "Access-Control-Allow-Origin" as NSObject)
                
                let response: HTTPURLResponse = HTTPURLResponse(url: self.request.url!, statusCode: 200, httpVersion: "HTTP/1.1", headerFields: headerFields as? [String:String])!
                self.client!.urlProtocol(self, didReceive: response, cacheStoragePolicy: .notAllowed)
                self.client!.urlProtocol(self, didLoad: resultData as Data)
                self.client!.urlProtocolDidFinishLoading(self)
            }
        }
    
    }
    

     caninit方法中return true表示进行请求,否则拦截请求。startLoading方法中创建相应的response返回给js中的request,注意finishLoading,否则容易造成死循环。

    UIWebview调用H5就相当简单了,一句代码搞定

      webView.stringByEvaluatingJavaScript(from: "javascript:window.goBack()");

  • 相关阅读:
    利用memcache实现,防止连续点击及每天点击次数
    Laravel 5.5 FormRequest 自定义表单请求验证类
    memcache安装及使用
    php查看当天访问量代码,以及每天访问量历史记录(本方法是存文件里,不是存数据库)
    SQL语句多个字段排序
    【C++】rand()函数,时间种子
    【C++】颜色的设置
    【堆栈应用一】一个数divided=几个最小质因数的乘积
    【JSP】中文乱码问题
    【汇编】MASM6.15几个简单的汇编程序
  • 原文地址:https://www.cnblogs.com/xiaoerheiwatu/p/10004409.html
Copyright © 2011-2022 走看看