使用AFNetworking, SDWebimage和OHHTTPStubs

来源:互联网 发布:str replace js 编辑:程序博客网 时间:2024/05/08 13:01

以下内容主要介绍:

  • 基于AFNetworking的HTTP操作,GET获取网页和JSON数据,上传文件,下载文件,以及加载图片
  • 基于SDWebimage的加载图片
  • 基于OHHTTPStubs的伪造网络响应用于测试(stub),而且可以模拟出网络的延时

    使用基于NSURLSession的AFNetworking API

    AFNetworking有2套用于网络操作的API:

    • 基于NSURLConnection
    • 基于NSURLSession

    后者是新的API,用于iOS 7 / Mac OS X 10.9及以上版本。

    这篇文章写的很好:从 NSURLConnection 到 NSURLSession,说明后者做了哪些改善和加强。

    现在越来越多的iOS项目最低要求iOS 7,让我们可以开始尝试使用这种新的方式。

    GET请求,获取普通网页文本

    AFHTTPSessionManager是使用NSURLSession的API。

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    NSURL *baseURL = [NSURL URLWithString:@"http://localhost/"];

    //设置和加入头信息
    NSURLSessionConfiguration *config = [NSURLSessionConfiguration defaultSessionConfiguration];
    [config setHTTPAdditionalHeaders:@{ @"User-Agent" : @"My Browser"}];

    AFHTTPSessionManager *manager=[[AFHTTPSessionManager alloc] initWithBaseURL:baseURL sessionConfiguration:config];
    manager.responseSerializer = [AFHTTPResponseSerializer serializer];
    manager.responseSerializer.acceptableContentTypes = [NSSet setWithObject:@"text/html"];

    //设置GET请求的参数
    NSDictionary *params=[[NSDictionary alloc] initWithObjectsAndKeys:@"3",@"id",nil];

    //发起GET请求
    [manager GET:@"" parameters:params success:^(NSURLSessionDataTask *task, id responseObject) {
    NSLog(@"HTML: %@", [[NSString alloc]initWithData:responseObject encoding:NSUTF8StringEncoding]);
    } failure:^(NSURLSessionDataTask *task, NSError *error) {
    NSLog(@"visit error: %@",error);
    }];

    为了方便测试,这个示例里写了个简单的Web Server,httpServer.js。

    运行httpServer.js,需要安装node.js环境。然后:

    sudo node httpServer

    我使用了80端口,在Mac环境下是需要root权限的。

    GET请求,获取JSON数据

    方法和GET请求网页文本大同小异,个别参数或者设置对象上有不同:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    NSURL *baseURL = [NSURL URLWithString:@"http://localhost/"];
    NSURLSessionConfiguration *config = [NSURLSessionConfiguration defaultSessionConfiguration];
    [config setHTTPAdditionalHeaders:@{ @"User-Agent" : @"My Browser"}];

    AFHTTPSessionManager *manager=[[AFHTTPSessionManager alloc] initWithBaseURL:baseURL sessionConfiguration:config];
    NSDictionary *params=[[NSDictionary alloc] initWithObjectsAndKeys:@"8",@"id",nil];

    [manager GET:@"/json" parameters:params success:^(NSURLSessionDataTask *task, id responseObject) {
    NSDictionary * object=(NSDictionary *)responseObject;
    NSLog(@"response message: %@",object[@"message"]);
    } failure:^(NSURLSessionDataTask *task, NSError *error) {
    NSLog(@"visit error: %@",error);
    }];

    下载文件

    AFNetworking API会返回NSURLSessionDownloadTask,可用于网络请求的取消、暂停和恢复。

    其实上文中的GET方法也返回了这个对象,只不过下载文件时间可能会较长,有可能有这方面的需求。

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    NSURLSessionConfiguration *configuration = [NSURLSessionConfiguration defaultSessionConfiguration];
    AFURLSessionManager *manager = [[AFURLSessionManager alloc] initWithSessionConfiguration:configuration];

    NSURL *URL = [NSURL URLWithString:@"http://www.baidu.com/img/bdlogo.png"];
    NSURLRequest *request = [NSURLRequest requestWithURL:URL];

    NSURLSessionDownloadTask *downloadTask = [manager downloadTaskWithRequest:request progress:nil destination:^NSURL *(NSURL *targetPath, NSURLResponse *response) {
    NSURL *documentsDirectoryURL = [[NSFileManager defaultManager] URLForDirectory:NSDocumentDirectory inDomain:NSUserDomainMask appropriateForURL:nil create:NO error:nil];
    return [documentsDirectoryURL URLByAppendingPathComponent:[response suggestedFilename]];
    } completionHandler:^(NSURLResponse *response, NSURL *filePath, NSError *error) {
    NSLog(@"File downloaded to: %@", filePath);
    uploadFilePath=filePath;
    }];
    [downloadTask resume];

    使用AFNetworking加载图片

    需要引入一下:

    1
    #import <UIImageView+AFNetworking.h>

    然后,UIImageView就会有setImageWithURL供使用。

    1
    2
    NSURL *URL = [NSURL URLWithString:@"http://www.baidu.com/img/bdlogo.png"];
    [imageView setImageWithURL:URL];

    另外,提供了其他方法,可设置占位图片,图片下载成功和失败的处理,以及停止图片下载的方法。

    1
    2
    3
    4
    setImageWithURL:
    setImageWithURL:placeholderImage:
    setImageWithURLRequest:placeholderImage:success:failure:
    – cancelImageRequestOperation

    使用SDWebimage加载图片

    SDWebImage,调用方式和AFNetworking类似,功能更强大,使用也很普及。

    需要引入:

    1
    #import <SDWebImage/UIImageView+WebCache.h>

    代码:

    1
    2
    NSURL *URL = [NSURL URLWithString:@"http://www.sogou.com/images/logo/new/sogou.png"];
    [imageView sd_setImageWithURL:URL];

    下面是完整的方法列表:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    – sd_imageURL
    sd_setImageWithURL:
    sd_setImageWithURL:placeholderImage:
    sd_setImageWithURL:placeholderImage:options:
    sd_setImageWithURL:completed:
    sd_setImageWithURL:placeholderImage:completed:
    sd_setImageWithURL:placeholderImage:options:completed:
    sd_setImageWithURL:placeholderImage:options:progress:completed:
    sd_setImageWithPreviousCachedImageWithURL:andPlaceholderImage:options:progress:completed:
    sd_setAnimationImagesWithURLs:
    – sd_cancelCurrentImageLoad
    – sd_cancelCurrentAnimationImagesLoad

    比AFNetworking选项更多一些,比如可以设置SDWebImageOptions

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    typedef NS_OPTIONS(NSUInteger, SDWebImageOptions ) {
    SDWebImageRetryFailed = 1 < < 0,
    SDWebImageLowPriority = 1 < < 1,
    SDWebImageCacheMemoryOnly = 1 < < 2,
    SDWebImageProgressiveDownload = 1 < < 3,
    SDWebImageRefreshCached = 1 < < 4,
    SDWebImageContinueInBackground = 1 < < 5,
    SDWebImageHandleCookies = 1 < < 6,
    SDWebImageAllowInvalidSSLCertificates = 1 < < 7,
    };

    还有:typedef void(^SDWebImageDownloaderProgressBlock)(NSInteger receivedSize, NSInteger expectedSize)

    • 可以用一组图片生成动画:sd_setAnimationImagesWithURLs
    • 使用之前的Cache先显示图片?sd_setImageWithPreviousCachedImageWithURL,这个我从字面意思理解,还没有使用
    • 有个process block,sd_setImageWithURL:placeholderImage:options:progress:completed:,可以获得receivedSizeexpectedSize字节参数,用来显示进程百分比

    另外,iOS image caching. Libraries benchmark (SDWebImage vs FastImageCache),这篇文章测试和对比,结论也是SDWebimage更好一些。

    AFNetworking上传文件

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    NSMutableURLRequest *request = [[AFHTTPRequestSerializer serializer] multipartFormRequestWithMethod:@"POST" URLString:@"http://localhost/upload" parameters:nil constructingBodyWithBlock:^(id<AFMultipartFormData> formData) {
    [formData appendPartWithFileURL:uploadFilePath name:@"file" fileName:@"filename.jpg" mimeType:@"image/jpeg" error:nil];
    } error:nil];

    AFURLSessionManager *manager = [[AFURLSessionManager alloc] initWithSessionConfiguration:[NSURLSessionConfiguration defaultSessionConfiguration]];
    NSProgress *progress = nil;

    NSURLSessionUploadTask *uploadTask = [manager uploadTaskWithStreamedRequest:request progress:&progress completionHandler:^(NSURLResponse *response, id responseObject, NSError *error) {
    if (error) {
    NSLog(@"Error: %@", error);
    } else {
    NSLog(@"%@ %@", response, responseObject);
    }
    }];

    [uploadTask resume];

    可以通过NSProgress获取上传进度(具体怎么做呢?)

    使用OHHTTPStubs伪造HTTP响应

    OHHTTPStubs,可用来伪造HTTP响应,这样不依赖服务器端,iOS的开发人员就可以测试网络服务了。

    这个API,同时支持:

    • NSURLConnection
    • NSURLSession

    不过,有个问题需要注意,如果App要上AppStore,是不能连接OHHTTPStubs的。

    下面说下怎么使用,先看代码:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    NSString *baseUrl=@"http://localhost";

    //针对http://locahost/json请求的mock
    [OHHTTPStubs stubRequestsPassingTest:^BOOL(NSURLRequest *request) {
    NSDictionary *params=[NSDictionary objectsFromURLQueryString:request.URL.query];
    NSLog(@"id: %@",params[@"id"]);

    return [[request.URL absoluteString] rangeOfString:[NSString stringWithFormat:@"%@/json",baseUrl]].location==0;
    } withStubResponse:^OHHTTPStubsResponse*(NSURLRequest *request) {
    NSLog(@"reqeust: %@",request);
    NSString* fixture = OHPathForFileInBundle(@"test.json",nil);
    return [[OHHTTPStubsResponse responseWithFileAtPath:fixture
    statusCode:200 headers:@{@"Content-Type":@"text/json"}
    ]requestTime:0 responseTime:0];
    }];

    基本思路是,调用方法,通过2个回调(Block)实现对指定HTTP请求的响应伪造:

    • 是否是要拦截的请求
    • 拦截后,创建一个响应

    在上述代码里还演示了:

    • 如何从URL中提取GET请求的参数,这里用到了:URLQueryToCocoa
    • 使用本地文件作为JSON数据,加入到HTTP响应中
    • 可以设置请求和响应的延时,requestTime:0 responseTime:0,这个相当有用

    还可以用于伪造图片的响应,测试了一下,上述的AFNetworking以及SDWebimage都有效。

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    //GET image with sdwebimage
    [OHHTTPStubs stubRequestsPassingTest:^BOOL(NSURLRequest *request) {
    return [[request.URL absoluteString] isEqualToString:@"http://www.sogou.com/images/logo/new/sogou.png"];
    } withStubResponse:^OHHTTPStubsResponse*(NSURLRequest *request) {
    NSLog(@"reqeust: %@",request);
    NSString* fixture = OHPathForFileInBundle(@"taobao.png",nil);
    return [[OHHTTPStubsResponse responseWithFileAtPath:fixture
    statusCode:200 headers:@{@"Content-Type":@"image/png"}
    ]requestTime:0 responseTime:0];
    }];

    测试的时候,要注意,之前可能是通过真实网络获取的,因此会有缓存。需要把App删除,重新安装测试。

    应该能看到类似下面的效果(使用的时本地图片了):

    OHHTTPStubs这些代码,只需在App启动加载一次即可,可写在AppDelegate中:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    - (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions {
    [[AFNetworkReachabilityManager sharedManager] startMonitoring];
    [HttpMock initMock];
    ```

    代码屏蔽掉,自然就使用真实网络了。

    编译正式使用代码的时候,可以考虑条件编译。


    ## AFNetworking的网络监控API

    提供的`AFNetworkReachabilityManager`可以单独使用,很方便,用于监控网络变化。

    比如,可以在App启动后执行下面操作,启动监控器:
    • (BOOL)application:(UIApplication )application didFinishLaunchingWithOptions:(NSDictionary )launchOptions {
      [[AFNetworkReachabilityManager sharedManager] startMonitoring];
      1
      2
      3
      4
      5

      <p>
      ---


      在ViewController中:

    -(void)viewDidLoad
    {
    [[AFNetworkReachabilityManager sharedManager] setReachabilityStatusChangeBlock:^(AFNetworkReachabilityStatus status) {
    NSLog(@”Reachability: %@”, AFStringFromNetworkReachabilityStatus(status));
    }];
    }
    ```

    监听网络变化,做出相应的操作,比如弹出提示框。

    正式使用时:

    • 可考虑在AppDelegate中setReachabilityStatusChangeBlock,当状态变化后,通过NSNotification发出通知
    • 在各个ViewController的viewDidAppearviewWillDisappear中监听和取消监听通知

    这是设想,还没有付诸实施,也许还有问题。

    不过至少不应该像本例中的,在ViewController中使用setReachabilityStatusChangeBlock

0 0
原创粉丝点击