iOS中Objective-C与JavaScript之间相互调用的实现(实现了与Android相同的机制)
来源:互联网 发布:万德软件科技有限公司 编辑:程序博客网 时间:2024/05/21 08:01
最近在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代码应该在客户端以注入的形式加入。所以,我们先实现一下需要注入的代码:
- ;(function() {
- var messagingIframe,
- bridge = 'external',
- CUSTOM_PROTOCOL_SCHEME = 'jscall';
- if (window[bridge]) { return }
- function _createQueueReadyIframe(doc) {
- messagingIframe = doc.createElement('iframe');
- messagingIframe.style.display = 'none';
- doc.documentElement.appendChild(messagingIframe);
- }
- window[bridge] = {};
- var methods = [%@];
- for (var i=0;i<methods.length;i++){
- var method = methods[i];
- var code = "(window[bridge])[method] = function " + method + "() {messagingIframe.src = CUSTOM_PROTOCOL_SCHEME + ':' + arguments.callee.name + ':' + encodeURIComponent(JSON.stringify(arguments));}";
- eval(code);
- }
- //创建iframe,必须在创建external之后,否则会出现死循环
- _createQueueReadyIframe(document);
- //通知js开始初始化
- //initReady();
- })();
- - (void)webViewDidFinishLoad:(UIWebView *)webView {
- if (webView != _webView) { return; }
- //is js insert
- if (![[webView stringByEvaluatingJavaScriptFromString:[NSString stringWithFormat:@"typeof window.%@ == 'object'", kBridgeName]] isEqualToString:@"true"]) {
- //get class method dynamically
- unsigned int methodCount = 0;
- Method *methods = class_copyMethodList([self class], &methodCount);
- NSMutableString *methodList = [NSMutableString string];
- for (int i=0; i<methodCount; i++) {
- NSString *methodName = [NSString stringWithCString:sel_getName(method_getName(methods[i])) encoding:NSUTF8StringEncoding];
- [methodList appendString:@"\""];
- [methodList appendString:[methodName stringByReplacingOccurrencesOfString:@":" withString:@""]];
- [methodList appendString:@"\","];
- }
- if (methodList.length>0) {
- [methodList deleteCharactersInRange:NSMakeRange(methodList.length-1, 1)];
- }
- NSBundle *bundle = _resourceBundle ? _resourceBundle : [NSBundle mainBundle];
- NSString *filePath = [bundle pathForResource:@"WebViewJsBridge" ofType:@"js"];
- NSString *js = [NSString stringWithContentsOfFile:filePath encoding:NSUTF8StringEncoding error:nil];
- [webView stringByEvaluatingJavaScriptFromString:[NSString stringWithFormat:js, methodList]];
- }
- __strong typeof(_webViewDelegate) strongDelegate = _webViewDelegate;
- if (strongDelegate && [strongDelegate respondsToSelector:@selector(webViewDidFinishLoad:)]) {
- [strongDelegate webViewDidFinishLoad:webView];
- }
- }
该委托对加载完成的网页进行了js注入,将类中实现的方法添加到了js中。注意,以上代码在注入前需要判断是否已经注入,避免重复注入。接下来,我们可以再实现一个webview的委托来拦截重定向事件:
- - (BOOL)webView:(UIWebView *)webView shouldStartLoadWithRequest:(NSURLRequest *)request navigationType:(UIWebViewNavigationType)navigationType {
- if (webView != _webView) { return YES; }
- NSURL *url = [request URL];
- __strong typeof(_webViewDelegate) strongDelegate = _webViewDelegate;
- NSString *requestString = [[request URL] absoluteString];
- if ([requestString hasPrefix:kCustomProtocolScheme]) {
- NSArray *components = [[url absoluteString] componentsSeparatedByString:@":"];
- NSString *function = (NSString*)[components objectAtIndex:1];
- NSString *argsAsString = [(NSString*)[components objectAtIndex:2]
- stringByReplacingPercentEscapesUsingEncoding:NSUTF8StringEncoding];
- NSData *argsData = [argsAsString dataUsingEncoding:NSUTF8StringEncoding];
- NSDictionary *argsDic = (NSDictionary *)[NSJSONSerialization JSONObjectWithData:argsData options:kNilOptions error:NULL];
- //convert js array to objc array
- NSMutableArray *args = [NSMutableArray array];
- for (int i=0; i<[argsDic count]; i++) {
- [args addObject:[argsDic objectForKey:[NSString stringWithFormat:@"%d", i]]];
- }
- //ignore warning
- #pragma clang diagnostic ignored "-Warc-performSelector-leaks"
- SEL selector = NSSelectorFromString([args count]>0?[function stringByAppendingString:@":"]:function);
- if ([self respondsToSelector:selector]) {
- [self performSelector:selector withObject:args];
- }
- return NO;
- } else if (strongDelegate && [strongDelegate respondsToSelector:@selector(webView:shouldStartLoadWithRequest:navigationType:)]) {
- return [strongDelegate webView:webView shouldStartLoadWithRequest:request navigationType:navigationType];
- } else {
- return YES;
- }
- }
以上代码中对重定向的url进行了判断,如果符合我们事先定义的协议,就进行解析,否则就进行跳转。解析的时候以冒号分隔,取出函数名和参数列表,并调用相应的方法。至此,我们就在iOS中实现了与Android相同的调用机制。
为了方便大家使用,我将以上代码封装成了一个类,具体的使用方法见Demo。
需要注意的是:
1. 在实际应用中可能出现js执行顺序的问题,如果网页中的js在注入前先获取绑定的对象进行保存,是无法获取到的,因为这时候待绑定的对象为空。这就需要网页中js的初始化在注入之后,因此在上文代码中有一个initReady方法。
2. 由于performSelector最多只能包含两个参数,因此例子中是通过数组来传递参数列表的。
- iOS中Objective-C与JavaScript之间相互调用的实现(实现了与Android相同的机制)
- iOS中Objective-C与JavaScript之间相互调用的实现(实现了与Android相同的机制)
- iOS中Objective-C与JavaScript之间相互调用的实现(实现了与Android相同的机制)
- iOS中Objective-C与JavaScript之间相互调用的实现(实现了与Android相同的机制)
- iOS中Objective-C与JavaScript之间相互调用的实现
- Android中如何实现WebView与JavaScript的相互调用
- Android与JavaScript之间的相互调用
- iOS中使用KVC实现JSON数据与Objective-C实体对象之间的转换
- iOS中使用KVC实现JSON数据与Objective-C实体对象之间的转换
- Android加载网页JavaScript与Java之间的相互调用
- android与javascript实现相互调用
- android中实现JavaScript与Java之间实现互相调用
- iOS:JavaScript与Objective-C之间的通信
- iOS Object-C 与JavaScript的相互调用详解
- Python与C之间的相互调用
- JavaScript与Objective-C之间的通信
- JavaScript与Objective-C之间的通信
- JavaScript与Objective-C之间的通信
- 提取 Microsoft.ReportViewer等dll
- 【算法】5 传说中的快排是怎样的,附实现示例
- java中栈的简单操作
- IUSR和IIS_IUSRS
- @SuppressWarnings
- iOS中Objective-C与JavaScript之间相互调用的实现(实现了与Android相同的机制)
- ny-水池数目[深搜]
- 第十章,绝对值排序(C++)
- Android 屏幕适配(一)
- 线程函数的设计以及MsgWaitForMultipleObjects函数的使用要点
- ELF文件介绍及hook
- AFNetworking实现程序重新启动时的断点续传
- Jquery autocomplete插件的使用
- excel中以指定分隔符从右往左截取字符&多重判断