首页 > 代码库 > iOS 8:【转】使用AFNetworking, SDWebimage和OHHTTPStubs
iOS 8:【转】使用AFNetworking, SDWebimage和OHHTTPStubs
源地址:http://blog.shiqichan.com/using-afnetworking-sdwebimage-and-ohhttpstubs/
写了个示例,放在GitHub上。
这是运行的动画:
以下内容主要介绍:
- 基于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:
,可以获得receivedSize
和expectedSize
字节参数,用来显示进程百分比
另外,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 | - (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions { [[AFNetworkReachabilityManager sharedManager] startMonitoring]; [HttpMock initMock]; |
代码屏蔽掉,自然就使用真实网络了。
编译正式使用代码的时候,可以考虑条件编译。
AFNetworking的网络监控API
提供的AFNetworkReachabilityManager
可以单独使用,很方便,用于监控网络变化。
比如,可以在App启动后执行下面操作,启动监控器:
1 2 | - (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions { [[AFNetworkReachabilityManager sharedManager] startMonitoring]; |
在ViewController中:
1 2 3 4 5 6 | -(void)viewDidLoad { [[AFNetworkReachabilityManager sharedManager] setReachabilityStatusChangeBlock:^(AFNetworkReachabilityStatus status) { NSLog(@"Reachability: %@", AFStringFromNetworkReachabilityStatus(status)); }]; } |
监听网络变化,做出相应的操作,比如弹出提示框。
正式使用时:
- 可考虑在AppDelegate中
setReachabilityStatusChangeBlock
,当状态变化后,通过NSNotification
发出通知 - 在各个ViewController的
viewDidAppear
和viewWillDisappear
中监听和取消监听通知
这是设想,还没有付诸实施,也许还有问题。
不过至少不应该像本例中的,在ViewController中使用setReachabilityStatusChangeBlock
。
iOS 8:【转】使用AFNetworking, SDWebimage和OHHTTPStubs