您的位置:首页 > 移动开发 > Objective-C

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)时间.
MethodAvgMinMax
location.href1.440.7013.59
location.hash1.000.666.19
<a> 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+++++
<a> 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.<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.html
https://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
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: