JavaScript 与 Objective-C 通信
2015-08-03 15:47
761 查看
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
<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; } }
监听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 <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 引入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.<name>.postMessage(); function postMyMessage() { var message = { 'message' : 'Hello, World!', 'numbers' : [ 1, 2, 3 ] }; window.webkit.messageHandlers.myName.postMessage(message); }
参考资料
http://blog.persistent.info/2013/10/a-faster-uiwebview-communication.htmlhttps://github.com/mihaip/web-experiments/pull/1
http://www.bignerdranch.com/blog/javascriptcore-example/
http://oscaraperez.com/blog_assets/JavaScript%20with%20iOS7.pdf
http://blog.impathic.com/post/64171814244/true-javascript-uiwebview-integration-in-ios7
相关文章推荐
- Objective C: 浅复制与深复制
- javaBean与Map<String,Object>互转
- Windows下的Objective-C集成开发环境搭建(IDE)
- JXPath(1.3) - Modifying Object Graphs
- oc 用来取消某个延时的动作接口NSObject cancelPreviousPerformRequestsWithTarget
- ObjectARX非模态对话框为当前焦点
- object detection--decision forests(DF)--VJ
- php的get_object_vars函数
- The type java.lang.Object cannot be resolved. It is indirectly referenced from required .class files
- iOS开发系列--Objective-C之KVC、KVO
- objective-c 中随机数的用法 (3种:arc4random() 、random()、CCRANDOM_0_1() )
- objective-c内存解析
- iOS事件处理——Swift & Objective-C 表述
- Objective-C发展历史
- Objective-C发展历史
- ObjectARX下载ObjectARX Wizards
- 【Objective-C编程】Objective-C的基本数据类型
- [objective-c] 面向对象1
- objective C程序 封装、继承及多态
- [Objective-c] OC介绍