美文网首页
UIWebView 中JavaScript 与 Objectiv

UIWebView 中JavaScript 与 Objectiv

作者: zeqinjie | 来源:发表于2016-04-13 16:15 被阅读465次

    iOS7 之前

    Objective-C -> JavaScript

    UIWebView对象有以下方法

    - (NSString *)stringByEvaluatingJavaScriptFromString:(NSString *)script

    该方法能够执行一段JavaScript字符串, 并返回字符串类型的返回值. 例如:

    UIWebView *webView = [[UIWebView alloc] init];

    // result ==  @"3"

    NSString *result = [webView stringByEvaluatingJavaScriptFromString:@"1+2"];

    // 调用js 对象的方法

    NSString *result2 = [webView stringByEvaluatingJavaScriptFromString:

    @"window.objectApis.doSomething('hello')"];

    缺点

    以上方法有以下缺点:

    返回值类型只能是字符串类型

    Objective-C需要对字符串结果进行反序列化

    JavaScript可能需要对结果进行序列化

    调用JavaScript对象的方法时, 传入参数比较麻烦

    Objective-C需要对参数进行序列化

    JavaScript可能需要对字符串参数进行反序列化

    // 调用js 对象的方法

    NSString *result2 = [webView stringByEvaluatingJavaScriptFromString:

    @"window.objectApis.doSomething('hello \" world')"];

    JavaScript -> Objective-C

    URL请求截获

    在UIWebView的浏览器的JavaScript中, 没有相关的接口可以调用Objective-C的相 关方法. 一般采用JavaScript在浏览器环境中发出URL请求,Objective-C截获请 求以获取相关请求的思路. 在Objective-C中在实现UIWebViewDelegate时截获请求:

    - (BOOL)webView:(UIWebView *)webView shouldStartLoadWithRequest:(NSURLRequest *)request

    navigationType:(UIWebViewNavigationType)navigationType

    {

    NSString *url = request.URL;

    // if (url是自定义的JavaScript通信协议) {

    //

    // do something

    //

    // 返回 NO 以阻止 `URL` 的加载或者跳转

    // return NO;

    // }

    }

    Objective-C可以在webView:shouldStartLoadWithRequest:navigationType方法中可以返回NO以阻止URL的加载或者跳转.

    JavaScript有各种不同的方式发出URL请求:

    location.href : 修改window.location.href替换成一个合成的URL, 比如async://method:args

    location.hash : 修改window.location.hash

    click : 创建一个元素, 赋值href属性, 并调用其click()方法

    iframe.src : 创建一个iframe元素, 赋值src属性

    XHR sync/async : 创建一个XMLHttpRequest对象,open()中设置相关信息及是否异步, 并调用send()方法发出请求

    var linkNode = document.createElement("a");

    var pongUrl;

    var xhr = new XMLHttpRequest();

    var iframe = document.createElement("iframe");

    iframe.style.display = "none";

    function ping(mechanism, startTime) {

    pongUrl = "pong://" + startTime;

    switch (mechanism) {

    // location.href

    case Mechanism.LocationHref:

    location.href = pongUrl;

    break;

    // location.hash

    case Mechanism.LocationHash:

    location.hash = "#" + pongUrl;

    break;

    // click

    case Mechanism.LinkClick:

    linkNode.href = pongUrl;

    linkNode.click();

    break;

    // iframe. src

    case Mechanism.FrameSrc:

    iframe.src = pongUrl;

    document.body.appendChild(iframe);

    document.body.removeChild(iframe);

    break;

    // XHR sync/async

    case Mechanism.XhrSync:

    case Mechanism.XhrAsync:

    xhr.open("GET", pongUrl, mechanism == Mechanism.XhrAsync);

    xhr.send();

    break;

    }

    }

    监听Cookie

    在UIWebView中,Objective-C可以通过NSHTTPCookieManagerCookiesChangedNotification事件以监听cookie的变化.

    NSNotificationCenter *center = NSNotificationCenter.defaultCenter;

    [defaultCenter addObserverForName:NSHTTPCookieManagerCookiesChangedNotification

    object:nil

    queue:nil

    usingBlock:^(NSNotification *notification) {

    NSHTTPCookieStorage *cookieStorage = notification.object;

    // do something with cookieStorage

    }];

    当JavaScript修改document.cookie后,Objective-C可以通过分析cookie以得到信息.

    缺点

    无论是URL请求截获方式还是监听Cookie的方式, 都有以下缺点:

    整个过程是异步的, 不能同步

    在JavaScript中不能直接获取Objective-C处理的返回值

    需要Objective-C调用JavaScript层自己实现的api才能得到返回值

    使用callback比较麻烦

    需要在JavaScript上自己实现

    iOS 7+

    iOS7 引入了JavaScriptCore, 是的JavaScript和Objective-C可以互操作.

    Objective-C可以使用JSContext的evalueScript()方法调用JavaScript提供 的方法.

    #import

    ...

    UIWebView *webView = [[UIWebView alloc] init];

    JSContext *jsContext = [webView valueForPath: @"documentView.webView.mainFrame.javaScriptContext"];

    // call javascript

    [jsContext evalueScript: @"window.objectApis.doSomething()"];

    将实现JSExport协议的对象直接赋值给JSContext对象的属性即可暴露方法给JavaScript.

    // provide obj-c apis

    WBNativeApis *nativeApis = [[WBNativeApis alloc] init];

    jsContext[@"nativeApis"] = nativeApis;

    // `WBNativeApis` Class

    #import

    #import

    @protocol NativeApis

    -(void) logMessage: (NSString *) message;

    -(NSString *) version;

    // 异步

    -(void) asyncPrint: (NSString *) message;

    // callback

    -(void) asyncPrint: (NSString *) message callback: (JSValue *) callback;

    @end

    @interface WBNativeApis : NSObject

    @end

    在浏览器环境中使用JavaScript调用Objective-C的api

    window.nativeApis.logMessage('A message from javascript!');

    window.asyncPrintCallback('Message from javascript!', function (data) {

    var div = document.createElement('div');

    div.innerText = "Send message to native ok and get data from native";

    document.body.appendChild(div);

    });

    JavaScriptCore将各种类型数据在不同编程语言间做了转换, 可进行直接操作.

    Objective-C type  |  JavaScript type

    --------------------+---------------------

    nil        |    undefined

    NSNull      |        null

    NSString      |      string

    NSNumber      |  number, boolean

    NSDictionary    |  Object object

    NSArray      |    Array object

    NSDate      |    Date object

    NSBlock (1)  |  Function object (1)

    id (2)    |  Wrapper object (2)

    Class (3)    | Constructor object (3)

    性能测试

    在iPhone 5S (ios 7.1) 模拟器条件下测试各种通信方式一次通信花费的毫秒(ms)时间.

    MethodAvgMinMax

    location.href1.440.7013.59

    location.hash1.000.666.19

    click1.400.6615.29

    iframe.src1.471.055.41

    XHR sync1.360.853.44

    XHR async0.850.4614.96

    document.cookie0.420.211.59

    JavaScriptCore0.060.040.13

    从表格中可以看出,JavaScriptCore的通信方式性能最好.

    兼容性

    各种通信方式的兼容性如下(+表示支持,X表示不支持):

    Method/DeviceiOS4iOS5iOS6iOS7iOS8

    location.href+++++

    location.hash+++++

    click+++++

    iframe.src+++++

    XHR sync+X+++

    XHR async+X+++

    document.cookie++++X

    JavaScriptCoreXXX++

    WKWebView (iOS 8 + )

    iOS 8 引入WKWebView,WKWebView不支持JavaScriptCore的方式但提供message handler的方式为JavaScript与Objective-C通信.

    在Objective-C中使用WKWebView的以下方法调用JavaScript:

    - (void)evaluateJavaScript:(NSString *)javaScriptString

    completionHandler:(void (^)(id, NSError *))completionHandler

    如果JavaScript代码出错, 可以在completionHandler进行处理.

    在Objective-C中注册 message handler:

    // WKScriptMessageHandler protocol

    - (void)userContentController:(WKUserContentController *)userContentController

    didReceiveScriptMessage:(WKScriptMessage *)message

    {

    NSLog(@"Message: %@", message.body);

    }

    [userContentController addScriptMessageHandler:handler name:@"myName"];

    在JavaScript将信息发给Objective-C:

    // window.webkit.messageHandlers..postMessage();

    function postMyMessage() {

    var message = { 'message' : 'Hello, World!', 'numbers' : [ 1, 2, 3 ] };

    window.webkit.messageHandlers.myName.postMessage(message);

    相关文章

      网友评论

          本文标题:UIWebView 中JavaScript 与 Objectiv

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