码迷,mamicode.com
首页 > 编程语言 > 详细

UIWebView 中JavaScript 与 Objective-C 通信

时间:2014-12-29 16:52:56      阅读:341      评论:0      收藏:0      [点我收藏+]

标签:uiwebview   ios   javascript   objective-c   通信   


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‘)"];

缺点

以上方法有以下缺点:

  1. 返回值类型只能是字符串类型
    • Objective-C 需要对字符串结果进行反序列化
    • JavaScript 可能需要对结果进行序列化
  2. 调用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
  • <a> click : 创建一个<a> 元素, 赋值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;
        // <a> 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;
    }
}

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的方式, 都有以下缺点:

  1. 整个过程是异步的, 不能同步
  2. JavaScript中不能直接获取Objective-C处理的返回值
    • 需要Objective-C 调用JavaScript层自己实现的api才能得到返回值
  3. 使用callback 比较麻烦
    • 需要在JavaScript 上自己实现

iOS 7+

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

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

#import <JavaScriptCore/JavaScriptCore.h>

...

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 <Foundation/Foundation.h>
#import <JavaScriptCore/JavaScriptCore.h>

@protocol NativeApis <JSExport>

-(void) logMessage: (NSString *) message;
-(NSString *) version;
// 异步
-(void) asyncPrint: (NSString *) message;
// callback
-(void) asyncPrint: (NSString *) message callback: (JSValue *) callback;

@end

@interface WBNativeApis : NSObject <NativeApis>
@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)时间.

Method Avg Min Max
location.href 1.44 0.70 13.59
location.hash 1.00 0.66 6.19
<a> click 1.40 0.66 15.29
iframe.src 1.47 1.05 5.41
XHR sync 1.36 0.85 3.44
XHR async 0.85 0.46 14.96
document.cookie 0.42 0.21 1.59
JavaScriptCore 0.06 0.04 0.13

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

兼容性

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

Method/Device iOS4 iOS5 iOS6 iOS7 iOS8
location.href + + + + +
location.hash + + + + +
<a> click + + + + +
iframe.src + + + + +
XHR sync + X + + +
XHR async + X + + +
document.cookie + + + + X
JavaScriptCore X X X + +

WKWebView (iOS 8 + )

iOS 8 引入WKWebViewWKWebView 不支持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.<name>.postMessage();?

function postMyMessage()? {?
    var message = { ‘message‘ : ‘Hello, World!‘, ‘numbers‘ : [ 1, 2, 3 ] };?
    window.webkit.messageHandlers.myName.postMessage(message);?
}

参考资料

  1. http://blog.persistent.info/2013/10/a-faster-uiwebview-communication.html
  2. https://github.com/mihaip/web-experiments/pull/1
  3. http://www.bignerdranch.com/blog/javascriptcore-example/
  4. http://oscaraperez.com/blog_assets/JavaScript%20with%20iOS7.pdf
  5. http://blog.impathic.com/post/64171814244/true-javascript-uiwebview-integration-in-ios7

UIWebView 中JavaScript 与 Objective-C 通信

标签:uiwebview   ios   javascript   objective-c   通信   

原文地址:http://blog.csdn.net/fudesign2008/article/details/42239217

(0)
(0)
   
举报
评论 一句话评论(0
登录后才能评论!
© 2014 mamicode.com 版权所有  联系我们:gaon5@hotmail.com
迷上了代码!