People Lack Willpower,Rather Than Strength!
1.NSURLSession
- 本節主要涉及
- NSURLSession的兩個get請求/一個post請求
- NSURLSessionTask抽象類,及其三個具象類:
- 1.NSURLSessionDataTask
- 2.NSURLSessionDownloadTask
- 3.NSURLSessionUploadTask
1.1 NSURLSession 基本使用
-
使用步驟
- 創建NSURLSession
- 創建Task
- 執行Task
-
默認情況下創建的請求都是GET類型
- GET1 : 使用request創建task
// dataTask NSURL *url = [NSURL URLWithString:@"http://120.25.226.186:32812/login?username=520it&pwd=520it&type=JSON"]; NSURLRequest *request = [NSURLRequest requestWithURL:url]; // 1.創建session NSURLSession *session = [NSURLSession sharedSession]; // 2.使用session創建task NSURLSessionDataTask *task = [session dataTaskWithRequest:request completionHandler:^(NSData *data, NSURLResponse *response, NSError *error) { /* 參數說明: data:服務器返回給我們的數據; response:響應頭 error:錯誤信息 */ // 通過該方法,我們可以對data進行簡單處理 NSLog(@"%@",[[NSString alloc] initWithData:data encoding:NSUTF8StringEncoding]); // 2015-09-09 22:58:58.010 01-NSURLSession基本使用[3368:81761] {"success":"登錄成功"} }]; // 3.執行task [task resume];
- GET2: 使用URL創建Task
NSURL *url = [NSURL URLWithString:@"http://120.25.226.186:32812/login?username=520it&pwd=520it&type=JSON"]; // 1.session創建 NSURLSession *session = [NSURLSession sharedSession]; // 2.使用session創建task // 如果通過傳入url創建task,方法內部會自動創建一個request,略微省事; // 如果是發送get請求,或者不需要設置請求頭信息,那么建議使用當前方法發送請求?? NSURLSessionDataTask *task = [session dataTaskWithURL:url completionHandler:^(NSData *data, NSURLResponse *response, NSError *error) { NSLog(@"%@",[[NSString alloc] initWithData:data encoding:NSUTF8StringEncoding]); /* 2015-09-09 22:58:29.822 01-NSURLSession基本使用[3348:81294] {"success":"登錄成功"} */ }]; // 3.執行task [task resume];
-
POST方法
- 關鍵在于修改請求體中的HTTPMethod
NSURL *url = [NSURL URLWithString:@"http://120.25.226.186:32812/login"]; NSMutableURLRequest *request = [NSMutableURLRequest requestWithURL:url]; request.HTTPMethod = @"POST"; request.HTTPBody = [@"username=520it&pwd=520it&type=JSON" dataUsingEncoding:NSUTF8StringEncoding]; // 1.創建session NSURLSession *session = [NSURLSession sharedSession]; // 2.根據session,創建task NSURLSessionDataTask *task = [session dataTaskWithRequest:request]; NSLog(@"%@",task); //2015-09-09 22:57:28.105 01-NSURLSession基本使用[3322:80609] <__NSCFLocalDataTask: 0x7f86c170c850>{ taskIdentifier: 1 } { suspended } // 3.執行task [task resume];
1.2 NSURLSessionDownloadTask
NSURLSessionDownloadTask簡單使用
-
注意:
- 默認情況下,使用NSURLSessionDownloadTask時,系統已經幫我們實現邊下載邊存儲.防止內存暴增.
- 而需要我們做的只是將下載的資源從不安全的tmp文件夾挪到caches文件夾.
NSURL *url = [NSURL URLWithString:@"http://120.25.226.186:32812/resources/images/minion_02.png"]; NSURLRequest *request = [NSURLRequest requestWithURL:url]; // NSURLSessionDownloadTask // 1.創建session NSURLSession *session = [NSURLSession sharedSession]; // 2.根據session創建task NSURLSessionDownloadTask *task = [session downloadTaskWithRequest:request completionHandler:^(NSURL *location, NSURLResponse *response, NSError *error) { //查看是否下載成功.這里reponse正是類型是NSHTTPURLResponse?? NSHTTPURLResponse *resp = (NSHTTPURLResponse *)response; NSLog(@"%ld,%zd",resp.statusCode,resp.expectedContentLength); // 參數解釋: // location 即是下載好的資源在沙盒中的地址 // NSURLSessionDownloadTask已經默認幫我們實現了,邊下載邊寫入沙盒,以防內存暴增; // 查看默認下載地址 NSLog(@"%@",location); // 默認是在tmp文件夾下,不安全,我們需要做的只是把它移到我們需要的位置:caches // 使用NSFileManager NSFileManager *manager = [NSFileManager defaultManager]; // 將下載好的文件轉移至caches // NSString *toPath = [[location lastPathComponent] cacheDir]; NSString *toPath = [response.suggestedFilename cacheDir]; [manager moveItemAtURL:location toURL:[NSURL fileURLWithPath:toPath] error:nil]; } // 3.執行task [task resume]; //========================================== // 通過url創建task [session downloadTaskWithURL:<#(NSURL *)#> completionHandler:<#^(NSURL *location, NSURLResponse *response, NSError *error)completionHandler#>] // 通過上次下載中斷中處創建新task-->斷點下載 [session downloadTaskWithResumeData:<#(NSData *)#> completionHandler:<#^(NSURL *location, NSURLResponse *response, NSError *error)completionHandler#>];
NSURLSessionDownloadTask監聽下載進度
- downloadTask可以暫停/取消/恢復下載
- 這里,無論從suspended還是canle中resume,都是程序正常狀態下的;
- 如果程序意外中斷,downloadTask提供的方法還不足以完成,dataTask可以完成!??
- 注意: 使用代理方法時,不要使用block方法處理返回數據.否則代理方法失效.
- 監聽下載進度
- (void)viewDidLoad
{
[super viewDidLoad];
// 保存下載路徑
self.path = [@"minion_02.mp4" cacheDir];
}
/**開始下載 */
- (IBAction)download:(id)sender {
NSURL *url = [NSURL URLWithString:@"http://120.25.226.186:32812/resources/videos/minion_02.mp4"];
// 1.創建session
/*
第一個參數:Session的配置信息
第二個參數: 代理
第三個參數: 決定了代理方法在哪個線程中執行
*/
self.session = [NSURLSession sessionWithConfiguration:[NSURLSessionConfiguration defaultSessionConfiguration] delegate:self delegateQueue:[NSOperationQueue mainQueue]];
// 2.根據session創建downloadTask
self.task = [self.session downloadTaskWithURL:url];
// 注意:這里如果使用回調方法,那么代理方法就不起作用了!!!????
// [session downloadTaskWithURL:<#(NSURL *)#> completionHandler:<#^(NSURL *location, NSURLResponse *response, NSError *error)completionHandler#>]
// 3.執行Task
[self.task resume];
}
// ===========================================
// 接收到服務器反饋的數據是調用,開始寫入數據
/*注意:該方法會調用一次或者多次
bytesWritten:當前寫入數據量;
totalBytesWritten:總共寫入數據量;
totalBytesExpectedToWrite:服務器返回給我們的文件大小
*/
- (void)URLSession:(NSURLSession *)session downloadTask:(NSURLSessionDownloadTask *)downloadTask didWriteData:(int64_t)bytesWritten totalBytesWritten:(int64_t)totalBytesWritten totalBytesExpectedToWrite:(int64_t)totalBytesExpectedToWrite
{
self.progressView.progress = 1.0 * totalBytesWritten / totalBytesExpectedToWrite;
}
// 寫完數據調用
- (void)URLSession:(NSURLSession *)session downloadTask:(NSURLSessionDownloadTask *)downloadTask didFinishDownloadingToURL:(NSURL *)location
{
NSLog(@"didFinishDownloadingToURL");
// 數據寫入完成時,要把數據從tmp文件夾轉移至caches
NSFileManager *manager = [NSFileManager defaultManager];
NSURL *toUrl = [NSURL fileURLWithPath:self.path];
[manager moveItemAtURL:location toURL:toUrl error:nil];
}
// 下載完成
// 如果調用該方法時,error有值,表示下載出現錯誤
- (void)URLSession:(NSURLSession *)session task:(NSURLSessionTask *)task didCompleteWithError:(NSError *)error
{
NSLog(@"didCompleteWithError");
}
// 恢復下載時調用
- (void)URLSession:(NSURLSession *)session downloadTask:(NSURLSessionDownloadTask *)downloadTask didResumeAtOffset:(int64_t)fileOffset expectedTotalBytes:(int64_t)expectedTotalBytes
{
NSLog(@"didResumeAtOffset");
}
// ===================取消/恢復===================
/**暫停*/
- (IBAction)pause:(id)sender {
[self.task suspend];
}
/** 從暫停中恢復*/
- (IBAction)pause2goon:(id)sender {
[self.task resume];
}
/**取消 */
- (IBAction)cance:(id)sender {
// 注意這里如果使用這樣的取消,那么就沒辦法恢復了!??
// [self.task cancel];
// 如果是調用cancelByProducingResumeData方法, 方法內部會回調一個block, 在block中會將resumeData傳遞給我們
// resumeData中就保存了當前下載任務的配置信息(下載到什么地方, 從什么地方恢復等等)??
[self.task cancelByProducingResumeData:^(NSData *resumeData) {
self.resumeData = resumeData;
}];
}
/** 從取消中恢復*/
- (IBAction)cance2goon:(id)sender {
// 從上次中斷數據處新建下載任務
self.task = [self.session downloadTaskWithResumeData:self.resumeData];
[self.task resume];
}
1.3 NSURLSessionDataTask
NSURLSessionDataTask代理方法
- 注意:NSURLSessionDataTask的代理方法中,默認情況下是不接受服務器返回的數據的.如果想接受服務器返回的數據,必須手動告訴系統,我們需要接收數據??
- (void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event
{
NSURL *url = [NSURL URLWithString:@"http://120.25.226.186:32812/resources/images/minion_02.png"];
NSURLRequest *request = [NSURLRequest requestWithURL:url];
// 1.創建session
NSURLSession *session = [NSURLSession sessionWithConfiguration:[NSURLSessionConfiguration defaultSessionConfiguration] delegate:self delegateQueue:[NSOperationQueue mainQueue]];
// 2.根據session創建Task
NSURLSessionDataTask *task = [session dataTaskWithRequest:request];
// 3.執行Task
[task resume];
}
// ===================代理方法====================
#pragma mark - NSURLSessionDataDelegate
// 服務器響應時調用
- (void)URLSession:(NSURLSession *)session dataTask:(NSURLSessionDataTask *)dataTask didReceiveResponse:(NSURLResponse *)response completionHandler:(void (^)(NSURLSessionResponseDisposition))completionHandler
{
NSLog(@"didReceiveResponse");
// 參數解釋:??
// 系統默認是不會去調用didReceiveData和didCompleteWithError,必須手動告訴系統,我們需要接收數據
/* 可見:NSURLSessionResponseDisposition默認== 0 ,也就是說默認是不接收數據的??
typedef NS_ENUM(NSInteger, NSURLSessionResponseDisposition) {
NSURLSessionResponseCancel = 0, Cancel the load == -[task cancel]
NSURLSessionResponseAllow = 1, Allow the load to continue
NSURLSessionResponseBecomeDownload = 2, Turn this request into a download
*/
completionHandler(NSURLSessionResponseAllow);
}
// 收到服務器返回的數據時調用
- (void)URLSession:(NSURLSession *)session dataTask:(NSURLSessionDataTask *)dataTask didReceiveData:(NSData *)data
{
NSLog(@"didReceiveData");
}
// 請求完畢時調用,如果error有值,代表請求失敗
- (void)URLSession:(NSURLSession *)session task:(NSURLSessionTask *)task didCompleteWithError:(NSError *)error
{
NSLog(@"didCompleteWithError");
}
NSURLSessionDataTask斷點下載
- 說明:
- 由于dataTask并不擅長下載任務,所以如果用其完成斷點下載,那么還是得自己使用文件句柄,或者輸出流完成! 同時在request中設置下載位置.
- 顯然這并不是好的,但是使用擅長下載的downloadTask,貌似我們目前又很難從實現意外中斷恢復繼續下載;
- 注意: 這里是使用DataTask完成下載, 并不是專業的,無法從cancel重恢復.
- (void)viewDidLoad {
[super viewDidLoad];
// 初始化操作
// 1.初始化路徑
self.path = [@"" cacheDir];
// 2.初始化currentLength
self.currentLength = [self fileDataSize:self.path];
}
// ==============================================
#pragma mark - lazy
- (NSURLSession *)session
{
if (!_session) {
// 1.創建session
_session = [NSURLSession sessionWithConfiguration:[NSURLSessionConfiguration defaultSessionConfiguration] delegate:self delegateQueue:[NSOperationQueue mainQueue]];
}
return _session;
}
- (NSURLSessionDataTask *)task
{
if (!_task) {
//如果想實現意外中斷后的繼續下載,NSURLSessionDataTask也需要通過設置請求頭的Range來實現????
NSURL *url = [NSURL URLWithString:@"http://120.25.226.186:32812/resources/videos/minion_02.mp4"];
NSMutableURLRequest *request = [NSMutableURLRequest requestWithURL:url];
// 設置請求頭??????
NSString *range = [NSString stringWithFormat:@"bytes:%zd-",[self fileDataSize:self.path]];
[request setValue:range forHTTPHeaderField:@"Range"];
// 2.根據session創建Task
_task = [self.session dataTaskWithRequest:request];
}
return _task;
}
// 文件大小
- (NSUInteger)fileDataSize:(NSString *)path
{
NSFileManager *manager = [NSFileManager defaultManager];
NSDictionary *dict = [manager attributesOfItemAtPath:path error:nil];
return [dict[NSFileSize] integerValue];
}
// 輸出流獲得
- (NSOutputStream *)outputStream
{
if (!_outputStream) {
_outputStream = [NSOutputStream outputStreamToFileAtPath:self.path append:YES];
//輸出流開啟
[_outputStream open];
//NSLog(@"輸出流");
}
return _outputStream;
}
/** 開始下載
*/
- (IBAction)download:(id)sender {
[self.task resume];
}
// ======================代理方法====================
#pragma mark - NSURLSessionDataDelegate
// 服務器響應時調用
- (void)URLSession:(NSURLSession *)session dataTask:(NSURLSessionDataTask *)dataTask didReceiveResponse:(NSURLResponse *)response completionHandler:(void (^)(NSURLSessionResponseDisposition))completionHandler
{
completionHandler(NSURLSessionResponseAllow);
// 數據總大小
self.totalLength = response.expectedContentLength + [self fileDataSize:self.path];
// 感覺 self.currentLength = [self fileDataSize:self.path]??可以試試
}
// 收到服務器返回的數據時調用,調用一次或者多次
- (void)URLSession:(NSURLSession *)session dataTask:(NSURLSessionDataTask *)dataTask didReceiveData:(NSData *)data
{
// 輸出流寫入數據
[self.outputStream write:data.bytes maxLength:data.length];
// 計算進度
self.currentLength += data.length;
self.progressView.progress = 1.0 * self.currentLength / self.totalLength;
}
// 請求完畢時調用,如果error有值,代表請求失敗,由于沒有didFinish方法,所以一旦完成下載,就會掉該方法,和downloadTask不同!??
- (void)URLSession:(NSURLSession *)session task:(NSURLSessionTask *)task didCompleteWithError:(NSError *)error
{
// 關閉輸出流
[self.outputStream close];
}
1.4.NSURLSessionUploadTask
1.4.1.文件上傳
-
說明:
- 和NSURLConnection中的文件上傳區別在于:
- 設置請求體時,不可以request.HTTPBody = bodyData ; 需要將bodyData設置到創建的task中.
// 設置請求頭 request.HTTPMethod = @"POST"; [request setValue:[NSString stringWithFormat:@"multipart/form-data; boundary=%@", XMGBoundary] forHTTPHeaderField:@"Content-Type"]; // 設置請求體 NSMutableData *body = [NSMutableData data]; // 文件參數 // 分割線 [body appendData:XMGEncode(@"--")]; [body appendData:XMGEncode(XMGBoundary)]; [body appendData:XMGNewLine]; ..... // 注意這里通過設置請求體 = data完成文件上傳,官方說這樣做會被忽略 // 就是說, 如果利用NSURLSessionUploadTask上傳文件, 那么請求體必須寫在fromData參數中, 不能設置在request中. 否則設置在request中會被忽略 /* The body stream and body data in this request object are ignored. */ // request.HTTPBody = data; ? // 1.創建session NSURLSession *session = [NSURLSession sharedSession]; // 2.根據session創建Task //注意這里的data是文件參數和非文件參數的拼接二進制?? NSURLSessionUploadTask *task = [session uploadTaskWithRequest:request fromData: body completionHandler:^(NSData *data, NSURLResponse *response, NSError *error) { NSLog(@"%@",[[NSString alloc] initWithData:data encoding:NSUTF8StringEncoding]); }]; // 3.執行Task [task resume]; // 注意:不要使用這個方法. fromFile方法是用于PUT請求上傳文件的 // 而我們的服務器只支持POST請求上傳文件 [session uploadTaskWithRequest:request fromFile:fileUrl completionHandler:^(NSData *data, NSURLResponse *response, NSError *error) {}];?
- 和NSURLConnection中的文件上傳區別在于:
1.4.2.文件上傳的監聽
- 關鍵: 設置代理及實現對應代理方法即可
- 核心代碼:
........
// 1.創建session
NSURLSession *session = [NSURLSession sessionWithConfiguration:[NSURLSessionConfiguration defaultSessionConfiguration] delegate:self delegateQueue:[NSOperationQueue mainQueue]];
// 2.根據session創建Task
// 該方法中有回調函數,會影響代理方法調用??
// NSURLSessionUploadTask *task = [session uploadTaskWithRequest:request fromData:body completionHandler:^(NSData *data, NSURLResponse *response, NSError *error) { }];
NSURLSessionUploadTask *task = [session uploadTaskWithRequest:request fromData:body];
// 3.執行Task
[task resume];
// =================代理方法====================
#pragma mark - NSURLSessionTaskDelegate
// 上傳過程中調用
/*
bytesSent:當前這一次上傳的數據大小;
totalBytesSent:總共上傳數據大小
totalBytesExpectedToSend:需要上傳的文件大小
*/
- (void)URLSession:(NSURLSession *)session task:(NSURLSessionTask *)task didSendBodyData:(int64_t)bytesSent totalBytesSent:(int64_t)totalBytesSent totalBytesExpectedToSend:(int64_t)totalBytesExpectedToSend
{
NSLog(@"didSendBodyData");
NSLog(@"%f",1.0 * totalBytesSent/totalBytesExpectedToSend);
}
// 請求完畢時調用
- (void)URLSession:(NSURLSession *)session task:(NSURLSessionTask *)task didCompleteWithError:(NSError *)error
{
NSLog(@"didCompleteWithError");
}
2.AFN
2.1 NSURLConnection的封裝
- 關鍵: 拿到AFHTTPRequestOperationManager 對象
- get 方法
// 1.創建manager
AFHTTPRequestOperationManager *manager = [AFHTTPRequestOperationManager manager];
// 2.使用manager發送get任務
/*
NSString:需要請求的url地址字符串
parameters: 請求是需要傳遞的參數,需要以字典形式
success:請求成功時回調的函數
failure:請求失敗時的回調函數
*/
// 注意AFNetworking的get方法發送請求時,參數和資源地址是分開寫的!??
NSString *path = @"http://120.25.226.186:32812/login";
NSDictionary *para = @{
@"username":@"520it",
@"pwd":@"520it",
@"type":@"XML"
};
[manager GET:path parameters:para success:^(AFHTTPRequestOperation *operation, id responseObject) {
/*
responseObject:
這里默認服務器返回給我們的數據是JSON數據,然后會自動把數據轉換為OC對象;
如果真實返回類型不是JSON,那么默認情況下不會回調success block,直接回調failure block
*/
NSLog(@"%@",responseObject);
/*服務器返回數據是JSON數據時,打印如下:
2015-09-09 14:58:41.587 08-ANF基本使用[3605:115247] {
success = "\U767b\U5f55\U6210\U529f";
}
*/
} failure:^(AFHTTPRequestOperation *operation, NSError *error){
NSLog(@"error");
/*當服務器返回數據類型不是JSON時,直接回調failure函數
2015-09-09 15:00:14.309 08-ANF基本使用[3685:116977] error
*/
}];
- post 方法
// 1.創建requestOperationManager
AFHTTPRequestOperationManager *manager = [AFHTTPRequestOperationManager manager];
// 2.使用manager發送post請求
NSString *path = @"http://120.25.226.186:32812/login";
NSDictionary *para = @{
@"username":@"520it",
@"pwd":@"520it",
@"type":@"XML"
};
[manager POST:path parameters:para success:^(AFHTTPRequestOperation *operation, id responseObject) {
NSLog(@"%@",responseObject);
} failure:^(AFHTTPRequestOperation *operation, NSError *error) {
NSLog(@"error");
}];
2.2 NSURLSession的封裝
- 關鍵: 拿到AFHTTPSessionManager 對象
- get 方法
// 1.創建manager
AFHTTPSessionManager *manager = [AFHTTPSessionManager manager];
// 2.利用manager發送get請求
NSString *path = @"http://120.25.226.186:32812/login";
NSDictionary *para = @{
@"username":@"520it",
@"pwd":@"520it",
@"type":@"XML"
};
[manager GET:path parameters:para success:^(NSURLSessionDataTask *task, id responseObject) {
NSLog(@"%@",responseObject);
} failure:^(NSURLSessionDataTask *task, NSError *error) {
NSLog(@"error");
}];
- post 方法
// 1.創建manager
AFHTTPSessionManager *manager = [AFHTTPSessionManager manager];
// 2.利用manager發送post請求
NSString *path = @"http://120.25.226.186:32812/login";
NSDictionary *para = @{
@"username":@"520it",
@"pwd":@"520it",
@"type":@"XML"
};
[manager POST:path parameters:para success:^(NSURLSessionDataTask *task, id responseObject) {
NSLog(@"%@",responseObject);
} failure:^(NSURLSessionDataTask *task, NSError *error) {
NSLog(@"error");
}];
2.3 AFN下載
-
download
- 注意 : 該方法需要resume
// 1.創建manager AFHTTPSessionManager *manager = [AFHTTPSessionManager manager]; // 2.使用manager創建下載任務 NSURLRequest *request = [NSURLRequest requestWithURL:[NSURL URLWithString:@"http://120.25.226.186:32812/resources/images/minion_02.png"]]; NSURLSessionDownloadTask *task = [manager downloadTaskWithRequest:request progress:nil destination:^NSURL *(NSURL *targetPath, NSURLResponse *response) { // 請求完成的回調 // targetPath:文件默認下載后保存的路徑 // response:響應頭 // NSURL:block返回值,告訴AFN框架,是否需要將下載的文件轉移到其他地方 NSString *path = [response.suggestedFilename cacheDir]; return [NSURL fileURLWithPath:path]; } completionHandler:^(NSURLResponse *response, NSURL *filePath, NSError *error) { // 下載完成的回調 // filePath:移動之后的文件路徑 NSLog(@"filePath=%@",filePath); /* 2015-09-09 15:44:08.476 08-ANF基本使用[4101:136108] filePath=file:///Users/PlwNs/Library/Developer/CoreSimulator/Devices/80A80097-63B4-4AB9-8B6B-1A30CCF465BE/data/Containers/Data/Application/0D45FB84-ED1B-4D5B-8401-B9FF2A9AB386/Library/Caches/minion_02.png */ }]; // 3.恢復下載 [task resume];
-
監聽下載進度
-
關鍵: 需要使用KVO 進行屬性監聽.
- 主代碼
-(void)monitorDownloadProgress
{
// 1.創建sessionManager
AFHTTPSessionManager *manager = [AFHTTPSessionManager manager];
// 2.使用sessionManager創建task
NSURLRequest *request = [NSURLRequest requestWithURL:[NSURL URLWithString:@"http://120.25.226.186:32812/resources/videos/minion_02.mp4"]];NSProgress *progress = nil;
self.progress = progress;
//說明,這里任務被加入線程循環中,然后根據progress的地址,不斷根據下載進度不斷更新progress,所以我們才可以監聽進度??
NSURLSessionDownloadTask *task = [manager downloadTaskWithRequest:request progress:&progress destination:^NSURL *(NSURL *targetPath, NSURLResponse *response) {
NSString *path = [response.suggestedFilename cacheDir];
return [NSURL fileURLWithPath:path];
} completionHandler:^(NSURLResponse *response, NSURL *filePath, NSError error) {
NSLog(@"filePath=%@",filePath);
}];
// 上述方法只會被調用一次,無法監控progress,只能使用屬性和代理方法??;
// 不過可惜,這里無法設置代理,只能通過KVO,通過KVO,那么這里我們就無需定義屬性了!!!??
// 那么如何通過監控progress,拿到下載進度那?經查勘了解到,progress有兩個屬性
/NSProgress只是一個對象!如何跟蹤進度!-> KVO 對屬性變化的監聽!
@property int64_t totalUnitCount: 需要下載的文件的總大小
@property int64_t completedUnitCount:已經下載的文件大小
*/
// 讓self監控progress的屬性變化
[progress addObserver:self forKeyPath:@"completedUnitCount" options:NSKeyValueObservingOptionNew context:nil];// 3.task resume
[task resume];
/*結果如下
2015-09-09 16:12:26.451 08-ANF基本使用[4430:150386] 0.00027
2015-09-09 16:12:26.509 08-ANF基本使用[4430:150388] 0.00043
2015-09-09 16:12:26.585 08-ANF基本使用[4430:150386] 0.00088
...................
*/
}``` - KVO ```objc -(void)observeValueForKeyPath:(NSString *)keyPath ofObject:(id)object change:(NSDictionary *)change context:(void *)context { // 根據開發經驗,由于一個project中可能存在諸多被observed對象,所以在coding時,一定記得判斷object類型?????????? // 與跳轉相似 if ([object isKindOfClass:[NSProgress class]]) { NSProgress *p = (NSProgress *)object; // 下載進度 NSLog(@"%f",1.0 * p.completedUnitCount / p.totalUnitCount); // 獲得準確進度 /** 準確的獲得進度 localizedDescription 10% localizedAdditionalDescription completed 32,768 of 318,829 fractionCompleted 0.102776(completedUnitCount/totalUnitCount) */ NSLog(@"%@, %@, %f", p.localizedDescription, p.localizedAdditionalDescription, p.fractionCompleted); } } ``` - 勿忘移除監聽?? ```objc -(void)dealloc { [self.progress removeObserver:self forKeyPath:@"completedUnitCount"]; } ```
-
2.4 upload
- 注意: AFNetworking 框架上傳數據時,不是使用uploadTask ,如果這個,還得拼接上傳請求體格式????
- 我們使用AFN中的POST: constructingBodyWith: 方法
- (void)upload
{
// 1.創建manager
AFHTTPSessionManager *manager = [AFHTTPSessionManager manager];
// 2.利用manager post文件
NSString *path = @"http://120.25.226.186:32812/upload";
NSDictionary *para = @{
@"username":@"pj"
};
/*
參數說明:
參數一:上傳服務器地址;
參數二:非文件參數;
formData:用來存儲需要用來上傳的文件二進制數據
*/
[manager POST:path parameters:para constructingBodyWithBlock:^(id<AFMultipartFormData> formData) {
// 在這個block中上傳文件數據
// formData就是專門用于保存需要上傳文件的二進制數據的
// formData如何存儲數據?三個方法:
// 1.appendPartWithFileData:
/*參數說明
NSData: 需要上傳的文件二進制數據
name: 上傳服務器字段(服務器對應的參數名稱)
fileName:服務器上保持該文件的名稱
mimeType:文件content-type(MIMETYPE)
*/
NSData *data = [NSData dataWithContentsOfFile:@"/Users/PlwNs/Desktop/座次表.png"];
[formData appendPartWithFileData:data name:@"file" fileName:@"table.png" mimeType:@"image/png"];
/*
2015-09-09 17:04:41.693 08-ANF基本使用[4692:168259] 成功回調{
success = "\U4e0a\U4f20\U6210\U529f";
}
*/
//--------------------------------------------------
// 2.appendPartWithFileURL:
NSURL *url = [NSURL fileURLWithPath:@"/Users/PlwNs/Desktop/座次表.png"];
[formData appendPartWithFileURL:url name:@"file" error:nil];
/*
2015-09-09 17:08:15.797 08-ANF基本使用[4770:170215] 成功回調{
success = "\U4e0a\U4f20\U6210\U529f";
}
*/
//---------------------------------------------------
// 3.appendPartWithFileURL:
NSURL *url = [NSURL fileURLWithPath:@"/Users/PlwNs/Desktop/座次表.png"];
[formData appendPartWithFileURL: url name:@"file" fileName:@"abc.png" mimeType:@"image/png" error:nil];
/*
2015-09-09 17:09:50.544 08-ANF基本使用[4806:171112] 成功回調{
success = "\U4e0a\U4f20\U6210\U529f";
}
*/
//---------------------------------------------------
// 4.注意該方法不是用來上傳數據的??
// [formData appendPartWithFormData:<#(NSData *)#> name:<#(NSString *)#>]
// } success:^(NSURLSessionDataTask *task, id responseObject) {
//} failure:^(NSURLSessionDataTask *task, NSError *error) {
//}];
// 這個方法也不行,還得拼接二進制data,太麻煩了!!??
// NSURLSessionUploadTask *task = [manager uploadTaskWithRequest:request fromData:<#(NSData *)#> progress:<#(NSProgress *__autoreleasing *)#> completionHandler:<#^(NSURLResponse *response, id responseObject, NSError *error)completionHandler#>];
// 這個是PUT請求,post請求不能使用??
// [manager uploadTaskWithRequest:<#(NSURLRequest *)#> fromFile:<#(NSURL *)#> progress:<#(NSProgress *__autoreleasing *)#> completionHandler:<#^(NSURLResponse *response, id responseObject, NSError *error)completionHandler#>];
}
2.5 序列化
- JSON數據
- (void)serializerJSON
{
// 1.創建manager
AFHTTPSessionManager *manager = [AFHTTPSessionManager manager];
// 如果現實不出來,加上下面兩句話?? 一般情況不會解析不出來,因為AF框架默認就是解析JSON數據.
manager.responseSerializer = [AFJSONResponseSerializer serializer];
[AFJSONResponseSerializer serializer].acceptableContentTypes = [NSSet setWithObject:@"text/json"];
// 2.根據manager執行post login請求
NSString *path = @"http://120.25.226.186:32812/login";
NSDictionary *paraDict = @{
@"username":@"520it",
@"pwd":@"520it",
@"type":@"JSON"
};
[manager POST:path parameters:paraDict success:^(NSURLSessionDataTask *task, id responseObject) {
NSLog(@"%@",responseObject);
/*很顯然,被AFN框架自動轉換成字典對象了
2015-09-09 18:02:24.647 08-ANF基本使用[5863:209466] {
success = "\U767b\U5f55\U6210\U529f";
}
*/
// 如果上面字典中type 不是 JSON , 那么直接進入failure!除非提前說明.
} failure:^(NSURLSessionDataTask *task, NSError *error) {
NSLog(@"error");
}];
}
- XML數據
- 如果服務器返回的數據不是JSON,我們如何提前通知AFN框架?
- 如果提前告知AFN框架服務器返回數據是XML類型,那么框架就會將返回一個解析器對象(也作出了處理)??
- (void)serializerXML
{
// 1.創建manager
AFHTTPSessionManager *manager = [AFHTTPSessionManager manager];
// 告訴AFN框架,服務器返回數據類型
// 1.1 AFN將服務器返回數據看做是XML類型,不做處理
manager.responseSerializer = [AFXMLParserResponseSerializer serializer];
// 如果還出錯,加下面這句話??
[AFXMLParserResponseSerializer serializer].acceptableContentTypes = [NSSet setWithObject:@"text/xml"];;
// 2.根據manager執行post login請求
NSString *path = @"http://120.25.226.186:32812/login";
NSDictionary *paraDict = @{
@"username":@"520it",
@"pwd":@"520it",
@"type":@"XML"
};
[manager POST:path parameters:paraDict success:^(NSURLSessionDataTask *task, id responseObject) {
//只要設置AFN的responseSerializer為XML, 那么返回的responseObject就是NSXMLParser解析器,而不是數據對象了??
NSLog(@"%@",responseObject);
/*
08-ANF基本使用[6150:229599] <NSXMLParser: 0x7fa95ccc9920>
*/
// 這里可以再解析
// NSXMLParser *parser = (NSXMLParser *)responseObject;
// parser.delegate = self;
// [parser parse];
} failure:^(NSURLSessionDataTask *task, NSError *error) {
NSLog(@"error");
}];
}
- 二進制數據
- 如果提前告知AFN框架服務器返回數據是二進制類型,也就是說不做任何處理??
- (void)serializer
{
// 1.創建manager
AFHTTPSessionManager *manager = [AFHTTPSessionManager manager];
// 告訴AFN框架,服務器返回數據類型
// 1.2 AFN將服務器返回數據看做是二進制類型,也就是說不做任何處理??
manager.responseSerializer = [AFHTTPResponseSerializer serializer];
// 2.根據manager執行post login請求
// 測試下載
NSString *path = @"http://120.25.226.186:32812/resources/images/minion_02.png";
// 這種寫法POST/GET都適用??
// POST方法為什么不行!!!??????
// -------->模擬器問題,reset下就好了他媽的干干干.....
[manager POST:path parameters:nil success:^(NSURLSessionDataTask *task, id responseObject) {
NSLog(@"%@",responseObject);
/* 結果:
2015-09-09 19:44:02.707 08-ANF基本使用[6642:243829] <3c21444f 43545950 45206874 6d6c2050 55424c49 4320222d 2f2f5733 432f2f44 54442048 544d4c20 342e3031 20547261 6e736974 696f6e61 6c2f2f45 4e222022 68747470 3a2f2f77 77772e77 332e6f72 672f5452 ...........
*/
} failure:^(NSURLSessionDataTask *task, NSError *error) {
NSLog(@"error");
}];
}
2.6 AFN 解耦
- KEY :
- 自定義單例繼承Manager
- 優點: 替換框架只需要求改單例類即可
- NSURLConnection 封裝
// PJNetworkingTool1.h 文件==================
#import "AFHTTPRequestOperationManager.h"
@interface PJNetworkTool1 : AFHTTPRequestOperationManager
- (instancetype)shareManager;
@end
// PJNetworkingTool1.m文件==================
#import "PJNetworkTool1.h"
@implementation PJNetworkTool1
- (instancetype)shareManager
{
static id instance = nil;
static dispatch_once_t onceToken;
dispatch_once(&onceToken, ^{
// 一般來說,為了重構,我們不這樣寫!??
// instance = [self shareManager];
NSString *urlStr = @"http://120.25.226.186:32812/";
instance = [[PJNetworkTool1 alloc] initWithBaseURL:[NSURL URLWithString:urlStr]];
});
return instance;
}
@end
- NSURLSession 封裝
// .h文件
#import "AFHTTPSessionManager.h"
@interface PJNetworkTool2 : AFHTTPSessionManager
- (instancetype)shareManager;
@end
// .m 文件
#import "PJNetworkTool2.h"
@implementation PJNetworkTool2
- (instancetype)shareManager
{
static id instance = nil;
static dispatch_once_t onceToken;
dispatch_once(&onceToken, ^{
NSString *urlStr = @"http://120.25.226.186:32812/";
instance = [[PJNetworkTool2 alloc] initWithBaseURL:[NSURL URLWithString:urlStr] sessionConfiguration:[NSURLSessionConfiguration defaultSessionConfiguration]];
});
return instance;
}
@end
2.7 AFN問題
- Xcode 7之后,使用手動導入AFNetworking,會有問題:
- 不使用Cocoapods時,post下載總是不成功的!!
3.網絡監測
3.1 蘋果官方做法
//蘋果自家的網絡監控
#import "Reachability.h"
- (void)viewDidLoad {
[super viewDidLoad];
// 1.創建reachability對象(蜂窩網/局域網都行)
self.network = [Reachability reachabilityForLocalWiFi];
// 2.讓self通過通知中心監聽reachability狀態變化
[[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(getNetworkStatus) name:kReachabilityChangedNotification object:nil];
// 3.reachability開始發通知
[self.network startNotifier];
}
- (void)dealloc
{
[[NSNotificationCenter defaultCenter] removeObserver:self];
}
- (void)getNetworkStatus
{
// 判斷蜂窩網是否可得
if ([Reachability reachabilityForInternetConnection].currentReachabilityStatus != NotReachable) {
NSLog(@"當前為蜂窩網");
}else if([Reachability reachabilityForLocalWiFi].currentReachabilityStatus != NotReachable){ // 判斷局域網是否可得
NSLog(@"當前為局域網");
}else{
NSLog(@"沒有網絡");
}
}
3.2 AFNetworking
- (void)AFMonitorNetwork
{
// 首先看看AFN框架如何做到監控網絡狀態
// 1.創建網絡監聽管理者
// 單例!
AFNetworkReachabilityManager *manager = [AFNetworkReachabilityManager sharedManager];
//AFNetworkReachabilityManager *manager1 = [AFNetworkReachabilityManager sharedManager];
//NSLog(@"%@\n%@",manager,manager1);
/*
<AFNetworkReachabilityManager: 0x7ff5618ab3a0>
<AFNetworkReachabilityManager: 0x7ff5618ab3a0>
*/
// 2.設置網絡變化時的回調block
/*
AFNetworkReachabilityStatusUnknown = 不能識別,
AFNetworkReachabilityStatusNotReachable = 沒有網絡,
AFNetworkReachabilityStatusReachableViaWWAN = 蜂窩網,
AFNetworkReachabilityStatusReachableViaWiFi = 局域網,
*/
[manager setReachabilityStatusChangeBlock:^(AFNetworkReachabilityStatus status) {
switch (status) {
case AFNetworkReachabilityStatusReachableViaWWAN:
NSLog(@"蜂窩網");
break;
case AFNetworkReachabilityStatusReachableViaWiFi:
NSLog(@"局域網");
break;
case AFNetworkReachabilityStatusNotReachable:
NSLog(@"沒有網");
break;
default:
NSLog(@"不能識別");
break;
}
}];
// 3.開始監聽:這樣就持續不斷的監聽了.....o(╯□╰)o
[manager startMonitoring];
/*2015-09-09 22:16:44.966 10-網絡監測[2658:55183] 局域網*/
}