首页 > 代码库 > IOS中js与本地通信手记

IOS中js与本地通信手记

如果只有一个webview要想js与ios的本地代码进行通信可以使用现成的phonegap框架,现因项目要求开发ipad版本的应用,页面中有多个需要多个webview所以需要自己编写js代码实现js与本地代码通信。

基本原理是:webview访问特殊的url前缀地址,然后在UIWebViewDelegate的shouldStartLoadWithRequest方法中对url进行过滤就可以达到js与本地代码进行通信了。

1.在MainViewController中实现UIWebViewDelegate,实现shouldStartLoadWithRequest方法:

<p class="p1">#define CALLFUNCTION_PREFIX @<span class="s1">"callfunction://"</span></p>
- (BOOL)webView:(UIWebView *)webView shouldStartLoadWithRequest:(NSURLRequest *)request navigationType:(UIWebViewNavigationType)navigationType{
    [[UIApplication sharedApplication] sendAction:@selector(resignFirstResponder) to:nil from:nil forEvent:nil];
    NSString *url = [request.URL absoluteString];
    NSLog(@"should url = %@ webView.tag = %d",url,webView.tag);
    
    if ([url hasPrefix:@"http://"] || [url hasPrefix:@"https://"]) {
        return [self logoutWithAlertWebView:webView url:url];
    }else if([url hasPrefix:CALLFUNCTION_PREFIX]){
        NSLog(@"webView.tag = %d",webView.tag);
        [[BasicPlugin getInstance]executePluginByUrl:url tag:webView.tag];
        return NO;
    }
    return YES;
}

2.以下代码通过类名和方法名反射执行指定类的指定方法:

-(void)executePluginByUrl:(NSString *)url tag:(NSInteger)tag{
    NSRange range = [url rangeOfString:CALLFUNCTION_PREFIX];
    NSString *temp = [url substringFromIndex:range.location + range.length];
    NSArray *arr = [temp componentsSeparatedByString:@"&"];
    
    NSString *callBackId = @"";
    NSString *className = @"";
    NSString *methodName = @"";
    NSMutableArray *params = [NSMutableArray arrayWithCapacity:0];
    
    if(arr != nil && arr.count > 0){
        NSString *tt = [arr objectAtIndex:0];
        NSArray *tempArr = [tt componentsSeparatedByString:@"="];
        callBackId = [tempArr objectAtIndex:1];
        
        tt = [arr objectAtIndex:1];
        tempArr = [tt componentsSeparatedByString:@"="];
        className = [tempArr objectAtIndex:1];
        
        tt = [arr objectAtIndex:2];
        tempArr = [tt componentsSeparatedByString:@"="];
        methodName = [tempArr objectAtIndex:1];
        
        tt = [arr objectAtIndex:3];
        tempArr = [tt componentsSeparatedByString:@"="];
        NSString *paramStr = [tempArr objectAtIndex:1];
        paramStr = [paramStr stringByReplacingPercentEscapesUsingEncoding:NSUTF8StringEncoding];
        if(paramStr != nil && paramStr.length > 0){
            params = [NSMutableArray arrayWithArray:[paramStr componentsSeparatedByString:@"$"]];
        }

        //反射调用有参方法
        NSMutableArray *pp = [NSMutableArray arrayWithCapacity:0];
        [pp addObject:[NSString stringWithFormat:@"%d",tag]];
        [pp addObject:callBackId];
        for(NSString *t in params){
            [pp addObject:t];
        }
        
        Class cls = NSClassFromString(className);
        id a= [[cls  alloc]  init];
        SEL selector = NSSelectorFromString([NSString stringWithFormat:@"%@%@",methodName,@":"]);
        if([a respondsToSelector:selector]){
            @try {
                [a performSelector:selector withObject:pp];
            }
            @catch (NSException *exception) {
                NSLog(@"BasicPlugin: class:%@'s method:%@ is not found.",className,methodName);
            }
        }
    }

3.js插件编写,使用location.href访问指定的url,这里有一个bug就是,不能同时调用两个插件,因为第二个插件的location.href会覆盖第一个location.href,所以需要等待第一个执行完才可以执行第二个。

//定义插件
$(function(){
  //
  var js2native_exception_arr = ["HomeViewPluginshowMask","HomeViewPluginunMask","HomeViewPluginalert"];
  function pageName(){
    var strUrl=location.href;
    var arrUrl=strUrl.split("/");
    var strPage=arrUrl[arrUrl.length-1];
    return strPage;
  }
  
  
  if(!window.plugins){
    window.plugins = {};
  }
  
  if(!window.js2native){
    window.js2native = {
        exec : function(className,methodName,params){
              var arr = params;
              var arrStr = '';
              if(arr && arr.length > 0){
                  for(var tt in arr){
                      arrStr += '$';
                      arrStr += arr[tt];
                  }
                  arrStr = arrStr.substring(1);
              }
              var url="callfunction://callbackId=" + className + methodName +"Event&className="+className+"&method="+methodName + "¶ms=" + encodeURIComponent(arrStr);
              url += ("¤tPage=" + pageName());
              url += ("&tt=" + new Date().getTime());
              var temp = className + methodName;
              for(var tt in js2native_exception_arr){
                var et = js2native_exception_arr[tt];
                if(et == temp){
                    url = "http://" + url;
                    $.get(url);
                    return;
                }
              }
              location.href = http://www.mamicode.com/url;>
4.其中js2native_exception_arr是为了异步访问后台,需要与NSURLCache一同使用。

@interface LocalSubstitutionCache : NSURLCache<NSURLConnectionDataDelegate>{
	NSMutableDictionary *cachedResponses;
}

- (NSCachedURLResponse *)cachedResponseForRequest:(NSURLRequest *)request{
	//
	// Get the path for the request
	//
    NSString *pathString = [[request URL] absoluteString];
    NSString *relativePath=[[request URL] relativePath];
    NSString *version = [[request URL] query];
    NSLog(@"LocalSubstitutionCache===%@",pathString);
    
    
    if(pathString != nil && ![@""isEqualToString:pathString] && [pathString hasPrefix:EXCEPTION_CALLFUNCTION_PREFIX]){
        pathString = [pathString substringFromIndex:[EXCEPTION_CALLFUNCTION_PREFIX length]];
        pathString = [NSString stringWithFormat:@"%@%@",EXCEPTION_CALLFUNCTION_REPLACE,pathString];
        [[BasicPlugin getInstance]executePluginByUrl:pathString tag:1];
        NSLog(@"cachedResponseForRequest.executePluginByUrl = %@",pathString);
        return [super cachedResponseForRequest:request];
    }

......






IOS中js与本地通信手记