打开APP
userphoto
未登录

开通VIP,畅享免费电子书等14项超值服

开通VIP
iOS中Objective

最近在iOS项目中需要使用到oc与js之间的相互调用,而且要求是实现方式必须与Android中的相同,方便js中统一处理。于是在对第三方库WebViewJavascriptBridge进行研究之后,仿照Android中的WebView与JS的交互机制,实现了一个,在这里分享给大家。

首先要说明的是,在iOS中js调用Objective-C的代码只能通过重定向的形式进行,即js中通过修改iframe的src,或者直接跳转到一个url,在Objective-C中通过UIWebView的

webView:shouldStartLoadWithRequest:navigationType:方法拦截这个跳转,然后通过解析跳转的url获取js需要调用的方法名和参数。而在Android中,只需要调用WebView的addJavascriptInterface方法,将一个js对象绑定到一个java类,在类中实现相应的函数,当js需要调用java的方法时,只需要直接在js中通过绑定的对象调用相应的函数即可。

显然Android中js交互的方式要比iOS上方便得多,因此,我们可以在iOS上实现一套与Android相类似的机制。下面先说明一下实现的原理,要在js中直接通过绑定的对象调用相应的函数,那么就需要在js中添加相应的代码,但是为了确保与Android的一致性,js代码应该在客户端以注入的形式加入。所以,我们先实现一下需要注入的代码:

01.;(function() {
02.var messagingIframe,
03.bridge = 'external',
04.CUSTOM_PROTOCOL_SCHEME = 'jscall';
05. 
06.if (window[bridge]) { return }
07. 
08.function _createQueueReadyIframe(doc) {
09.messagingIframe = doc.createElement('iframe');
10.messagingIframe.style.display = 'none';
11.doc.documentElement.appendChild(messagingIframe);
12.}
13.window[bridge] = {};
14.var methods = ["test1", "test2"];
15.for (var i=0;i<methods.length;i++){
16.var method = methods[i];
17.var code = "(window[bridge])[method] = function " + method + "() {messagingIframe.src = CUSTOM_PROTOCOL_SCHEME + ':' + arguments.callee.name + ':' + encodeURIComponent(JSON.stringify(arguments));}";
18.eval(code);
19.}
20. 
21.//创建iframe,必须在创建external之后,否则会出现死循环
22._createQueueReadyIframe(document);
23.//通知js开始初始化
24.//initReady();
25.})();

在以上代码中,我们在网页中添加了一个iframe,使得改变iframe的src时webview可以捕捉到重定向请求,使用这种方式进行重定向,相比直接修改网页的url要安全得多。同时设置了一个external对象,并为该对象绑定了test1、test2方法,当这两个方法执行时,会修改iframe的src,并将要调用的方法和参数以url的形式来构造。然后,我们需要在webview完成网页加载的时候进行注入,实现以下委托:

01.- (void)webViewDidFinishLoad:(UIWebView *)webView {
02.//js是否注入成功
03.if (![[webView stringByEvaluatingJavaScriptFromString:[NSString stringWithFormat:@"typeof window.%@ == 'object'", kBridgeName]] isEqualToString:@"true"]) {
04.NSBundle *bundle = _resourceBundle ? _resourceBundle : [NSBundle mainBundle];
05.NSString *filePath = [bundle pathForResource:@"WebViewJsBridge" ofType:@"js"];
06.NSString *js = [NSString stringWithContentsOfFile:filePath encoding:NSUTF8StringEncoding error:nil];
07.[webView stringByEvaluatingJavaScriptFromString:js];
08.}
09.}

注意,以上代码在注入前需要判断是否已经注入,避免重复注入。接下来,我们可以再实现一个webview的委托来拦截重定向事件:

01.- (BOOL)webView:(UIWebView *)webView shouldStartLoadWithRequest:(NSURLRequest *)request navigationType:(UIWebViewNavigationType)navigationType {
02.NSURL *url = [request URL];
03. 
04.NSString *requestString = [[request URL] absoluteString];
05.if ([requestString hasPrefix:kCustomProtocolScheme]) {
06.NSArray *components = [[url absoluteString] componentsSeparatedByString:@":"];
07. 
08.NSString *function = (NSString*)[components objectAtIndex:1];
09.NSString *argsAsString = [(NSString*)[components objectAtIndex:2]
10.stringByReplacingPercentEscapesUsingEncoding:NSUTF8StringEncoding];
11.NSData *argsData = [argsAsString dataUsingEncoding:NSUTF8StringEncoding];
12.NSDictionary *argsDic = (NSDictionary *)[NSJSONSerialization JSONObjectWithData:argsData options:kNilOptions error:NULL];
13.//将js的数组转换成objc的数组
14.NSMutableArray *args = [NSMutableArray array];
15.for (int i=0; i<[argsDic count]; i++) {
16.[args addObject:[argsDic objectForKey:[NSString stringWithFormat:@"%d", i]]];
17.}
18.//调用oc方法,忽略警告
19.#pragma clang diagnostic ignored "-Warc-performSelector-leaks"
20.SEL selector = NSSelectorFromString([function stringByAppendingString:@":"]);
21.CLog(@"sel:%@, args:%@", function, args);
22.if ([self respondsToSelector:selector]) {
23.[self performSelector:selector withObject:args];
24.}
25.return NO;
26.} else {
27.return YES;
28.}
29.}

以上代码中对重定向的url进行了判断,如果符合我们事先定义的协议,就进行解析,否则就进行跳转。解析的时候以冒号分隔,取出函数名和参数列表,并调用相应的方法。至此,我们就在iOS中实现了与Android相同的调用机制。

但以上方法的缺点是还没有达到Android中的方便程度,最理想的方法是当进行js注入时,动态设置待绑定的对象,同时动态获取当前类的实例方法,并生成相应的js代码。这就完全实现了Android中的绑定机制。这个实现起来并不难,有兴趣的朋友可以试试。

为了方便大家使用,我将以上代码封装成了一个类,具体的使用方法见Demo。

需要注意的是:

1. 在实际应用中可能出现js执行顺序的问题,如果网页中的js在注入前先获取绑定的对象进行保存,是无法获取到的,因为这时候待绑定的对象为空。这就需要网页中js的初始化在注入之后,因此在上文代码中有一个initReady方法。

2. 由于performSelector最多只能包含两个参数,因此例子中是通过数组来传递参数列表的。


如果大家觉得对自己有帮助的话,还希望能帮顶一下,谢谢:)个人博客:http://blog.csdn.net/zhaoxy2850本文地址:http://blog.csdn.net/zhaoxy_thu/article/details/22794201转载请注明出处,谢谢! 

本站仅提供存储服务,所有内容均由用户发布,如发现有害或侵权内容,请点击举报
打开APP,阅读全文并永久保存 查看更多类似文章
猜你喜欢
类似文章
关于UIWebView和PhoneGap的总结
iOS hybrid App 的实现原理及性能监测
JSContext監控UIWebView上JS事件,並執行JS方法,實現js與ios方法互調
利用C#开发移动跨平台Hybrid App(一):从Native端聊Hybrid的实现
iOS怎么和H5界面实现交互?求代码! | iOS开发
iOS与JavaScript交互总结
更多类似文章 >>
生活服务
热点新闻
分享 收藏 导长图 关注 下载文章
绑定账号成功
后续可登录账号畅享VIP特权!
如果VIP功能使用有故障,
可点击这里联系客服!

联系客服