热门标签 | HotTags
当前位置:  开发笔记 > 编程语言 > 正文

iOS中Objective-C与JavaScript之间相互调用的实现

原文地址:http:blog.csdn.netzhaoxy_thuarticledetails22794201最近在iOS项目中需要使用到oc与js之间的相互调用,而且要求是实现方式必须与

原文地址:http://blog.csdn.net/zhaoxy_thu/article/details/22794201

最近在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代码应该在客户端以注入的形式加入。所以,我们先实现一下需要注入的代码:

[Javascript] view plaincopy在CODE上查看代码片派生到我的代码片
  1. ;(function() {  
  2.     var messagingIframe,  
  3.         bridge = 'external',  
  4.         CUSTOM_PROTOCOL_SCHEME = 'jscall';  
  5.     
  6.     if (window[bridge]) { return }  
  7.   
  8.     function _createQueueReadyIframe(doc) {  
  9.         messagingIframe = doc.createElement('iframe');  
  10.         messagingIframe.style.display = 'none';  
  11.         doc.documentElement.appendChild(messagingIframe);  
  12.     }  
  13.     window[bridge] = {};  
  14.     var methods = [%@];  
  15.     for (var i=0;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对象,并为该对象绑定了若干方法,具体的方法之后会添加,当这两个方法执行时,会修改iframe的src,并将要调用的方法和参数以url的形式来构造。然后,我们需要在webview完成网页加载的时候进行注入,实现以下委托:

[objc] view plaincopy在CODE上查看代码片派生到我的代码片
  1. - (void)webViewDidFinishLoad:(UIWebView *)webView {  
  2.     if (webView != _webView) { return; }  
  3.     //is js insert  
  4.     if (![[webView stringByEvaluatingJavascriptFromString:[NSString stringWithFormat:@"typeof window.%@ == 'object'", kBridgeName]] isEqualToString:@"true"]) {  
  5.         //get class method dynamically  
  6.         unsigned int methodCount = 0;  
  7.         Method *methods = class_copyMethodList([self class], &methodCount);  
  8.         NSMutableString *methodList = [NSMutableString string];  
  9.         for (int i=0; i
  10.             NSString *methodName = [NSString stringWithCString:sel_getName(method_getName(methods[i])) encoding:NSUTF8StringEncoding];  
  11.             [methodList appendString:@"\""];  
  12.             [methodList appendString:[methodName stringByReplacingOccurrencesOfString:@":" withString:@""]];  
  13.             [methodList appendString:@"\","];  
  14.         }  
  15.         if (methodList.length>0) {  
  16.             [methodList deleteCharactersInRange:NSMakeRange(methodList.length-11)];  
  17.         }  
  18.           
  19.         NSBundle *bundle = _resourceBundle ? _resourceBundle : [NSBundle mainBundle];  
  20.         NSString *filePath = [bundle pathForResource:@"WebViewJsBridge" ofType:@"js"];  
  21.         NSString *js = [NSString stringWithContentsOfFile:filePath encoding:NSUTF8StringEncoding error:nil];  
  22.         [webView stringByEvaluatingJavascriptFromString:[NSString stringWithFormat:js, methodList]];  
  23.     }  
  24.   
  25.     __strong typeof(_webViewDelegate) strongDelegate = _webViewDelegate;  
  26.     if (strongDelegate && [strongDelegate respondsToSelector:@selector(webViewDidFinishLoad:)]) {  
  27.         [strongDelegate webViewDidFinishLoad:webView];  
  28.     }  
  29. }  

该委托对加载完成的网页进行了js注入,将类中实现的方法添加到了js中。注意,以上代码在注入前需要判断是否已经注入,避免重复注入。接下来,我们可以再实现一个webview的委托来拦截重定向事件:

[objc] view plaincopy在CODE上查看代码片派生到我的代码片
  1. - (BOOL)webView:(UIWebView *)webView shouldStartLoadWithRequest:(NSURLRequest *)request navigationType:(UIWebViewNavigationType)navigationType {  
  2.     if (webView != _webView) { return YES; }  
  3.     NSURL *url = [request URL];  
  4.     __strong typeof(_webViewDelegate) strongDelegate = _webViewDelegate;  
  5.       
  6.     NSString *requestString = [[request URL] absoluteString];  
  7.     if ([requestString hasPrefix:kCustomProtocolScheme]) {  
  8.         NSArray *components = [[url absoluteString] componentsSeparatedByString:@":"];  
  9.           
  10.         NSString *function = (NSString*)[components objectAtIndex:1];  
  11.         NSString *argsAsString = [(NSString*)[components objectAtIndex:2]  
  12.                                   stringByReplacingPercentEscapesUsingEncoding:NSUTF8StringEncoding];  
  13.         NSData *argsData = [argsAsString dataUsingEncoding:NSUTF8StringEncoding];  
  14.         NSDictionary *argsDic = (NSDictionary *)[NSJSONSerialization JSONObjectWithData:argsData options:kNilOptions error:NULL];  
  15.         //convert js array to objc array  
  16.         NSMutableArray *args = [NSMutableArray array];  
  17.         for (int i=0; i<[argsDic count]; i++) {  
  18.             [args addObject:[argsDic objectForKey:[NSString stringWithFormat:@"%d", i]]];  
  19.         }  
  20.         //ignore warning  
  21. #pragma clang diagnostic ignored "-Warc-performSelector-leaks"  
  22.         SEL selector = NSSelectorFromString([args count]>0?[function stringByAppendingString:@":"]:function);  
  23.         if ([self respondsToSelector:selector]) {  
  24.             [self performSelector:selector withObject:args];  
  25.         }  
  26.         return NO;  
  27.     } else if (strongDelegate && [strongDelegate respondsToSelector:@selector(webView:shouldStartLoadWithRequest:navigationType:)]) {  
  28.         return [strongDelegate webView:webView shouldStartLoadWithRequest:request navigationType:navigationType];  
  29.     } else {  
  30.         return YES;  
  31.     }  
  32. }  

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

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

需要注意的是:

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

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



推荐阅读
  • 开发笔记:加密&json&StringIO模块&BytesIO模块
    篇首语:本文由编程笔记#小编为大家整理,主要介绍了加密&json&StringIO模块&BytesIO模块相关的知识,希望对你有一定的参考价值。一、加密加密 ... [详细]
  • 本文介绍了OC学习笔记中的@property和@synthesize,包括属性的定义和合成的使用方法。通过示例代码详细讲解了@property和@synthesize的作用和用法。 ... [详细]
  • 本文讨论了在手机移动端如何使用HTML5和JavaScript实现视频上传并压缩视频质量,或者降低手机摄像头拍摄质量的问题。作者指出HTML5和JavaScript无法直接压缩视频,只能通过将视频传送到服务器端由后端进行压缩。对于控制相机拍摄质量,只有使用JAVA编写Android客户端才能实现压缩。此外,作者还解释了在交作业时使用zip格式压缩包导致CSS文件和图片音乐丢失的原因,并提供了解决方法。最后,作者还介绍了一个用于处理图片的类,可以实现图片剪裁处理和生成缩略图的功能。 ... [详细]
  • 篇首语:本文由编程笔记#小编为大家整理,主要介绍了预加载多个本地WebView相关的知识,希望对你有一定的参考价值。 ... [详细]
  • 本文介绍了闭包的定义和运转机制,重点解释了闭包如何能够接触外部函数的作用域中的变量。通过词法作用域的查找规则,闭包可以访问外部函数的作用域。同时还提到了闭包的作用和影响。 ... [详细]
  • Spring常用注解(绝对经典),全靠这份Java知识点PDF大全
    本文介绍了Spring常用注解和注入bean的注解,包括@Bean、@Autowired、@Inject等,同时提供了一个Java知识点PDF大全的资源链接。其中详细介绍了ColorFactoryBean的使用,以及@Autowired和@Inject的区别和用法。此外,还提到了@Required属性的配置和使用。 ... [详细]
  • SpringMVC接收请求参数的方式总结
    本文总结了在SpringMVC开发中处理控制器参数的各种方式,包括处理使用@RequestParam注解的参数、MultipartFile类型参数和Simple类型参数的RequestParamMethodArgumentResolver,处理@RequestBody注解的参数的RequestResponseBodyMethodProcessor,以及PathVariableMapMethodArgumentResol等子类。 ... [详细]
  • JavaWeb中读取文件资源的路径问题及解决方法
    在JavaWeb开发中,读取文件资源的路径是一个常见的问题。本文介绍了使用绝对路径和相对路径两种方法来解决这个问题,并给出了相应的代码示例。同时,还讨论了使用绝对路径的优缺点,以及如何正确使用相对路径来读取文件。通过本文的学习,读者可以掌握在JavaWeb中正确找到和读取文件资源的方法。 ... [详细]
  • 手把手教你使用GraphPad Prism和Excel绘制回归分析结果的森林图
    本文介绍了使用GraphPad Prism和Excel绘制回归分析结果的森林图的方法。通过展示森林图,可以更加直观地将回归分析结果可视化。GraphPad Prism是一款专门为医学专业人士设计的绘图软件,同时也兼顾统计分析的功能,操作便捷,可以帮助科研人员轻松绘制出高质量的专业图形。文章以一篇发表在JACC杂志上的研究为例,利用其中的多因素回归分析结果来绘制森林图。通过本文的指导,读者可以学会如何使用GraphPad Prism和Excel绘制回归分析结果的森林图。 ... [详细]
  • Servlet多用户登录时HttpSession会话信息覆盖问题的解决方案
    本文讨论了在Servlet多用户登录时可能出现的HttpSession会话信息覆盖问题,并提供了解决方案。通过分析JSESSIONID的作用机制和编码方式,我们可以得出每个HttpSession对象都是通过客户端发送的唯一JSESSIONID来识别的,因此无需担心会话信息被覆盖的问题。需要注意的是,本文讨论的是多个客户端级别上的多用户登录,而非同一个浏览器级别上的多用户登录。 ... [详细]
  • 本文介绍了Java后台Jsonp处理方法及其应用场景。首先解释了Jsonp是一个非官方的协议,它允许在服务器端通过Script tags返回至客户端,并通过javascript callback的形式实现跨域访问。然后介绍了JSON系统开发方法,它是一种面向数据结构的分析和设计方法,以活动为中心,将一连串的活动顺序组合成一个完整的工作进程。接着给出了一个客户端示例代码,使用了jQuery的ajax方法请求一个Jsonp数据。 ... [详细]
  • 微信官方授权及获取OpenId的方法,服务器通过SpringBoot实现
    主要步骤:前端获取到code(wx.login),传入服务器服务器通过参数AppID和AppSecret访问官方接口,获取到OpenId ... [详细]
  • 资源:吊炸天!74款APP完整源码!android界面中点击输入框时弹出输入法如果输入框在底部会出现输入法遮挡输入内容的问题解决办法设置activity的windowsoftinpu ... [详细]
  • Android Studio中的IBM MobileFirst Compile问题 - IBM MobileFirst Compile in Android Studio Issue
    IbuiltaMultipageapplicationbyusingIBMMobileFirst,accordingto据我所知,我使用IBMMobileFirst构建了一个 ... [详细]
  • 安卓开发入门!BAT大厂面试基础题集合,顺利通过阿里Android岗面试
    其实不是Android不行了,而是你跟不上了我的很多读者都在反馈说,现在一个岗位可以收到的简历数,是前几年的几倍。我们必须承认ÿ ... [详细]
author-avatar
Timeless
这个家伙很懒,什么也没留下!
PHP1.CN | 中国最专业的PHP中文社区 | DevBox开发工具箱 | json解析格式化 |PHP资讯 | PHP教程 | 数据库技术 | 服务器技术 | 前端开发技术 | PHP框架 | 开发工具 | 在线工具
Copyright © 1998 - 2020 PHP1.CN. All Rights Reserved | 京公网安备 11010802041100号 | 京ICP备19059560号-4 | PHP1.CN 第一PHP社区 版权所有