3.NSURLSession &AFN &網(wǎng)絡(luò)監(jiān)測

People Lack Willpower,Rather Than Strength!

1.NSURLSession

  • 本節(jié)主要涉及
    • NSURLSession的兩個(gè)get請(qǐng)求/一個(gè)post請(qǐng)求
    • NSURLSessionTask抽象類,及其三個(gè)具象類:
      • 1.NSURLSessionDataTask
      • 2.NSURLSessionDownloadTask
      • 3.NSURLSessionUploadTask

1.1 NSURLSession 基本使用

  • 使用步驟

    • 創(chuàng)建NSURLSession
    • 創(chuàng)建Task
    • 執(zhí)行Task
  • 默認(rèn)情況下創(chuàng)建的請(qǐng)求都是GET類型

    • GET1 : 使用request創(chuàng)建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.創(chuàng)建session
    NSURLSession *session = [NSURLSession sharedSession];
    // 2.使用session創(chuàng)建task
    NSURLSessionDataTask *task = [session dataTaskWithRequest:request completionHandler:^(NSData *data, NSURLResponse *response, NSError *error) {
    /*
      參數(shù)說明:
      data:服務(wù)器返回給我們的數(shù)據(jù);
      response:響應(yīng)頭
      error:錯(cuò)誤信息
      */
     // 通過該方法,我們可以對(duì)data進(jìn)行簡單處理
    NSLog(@"%@",[[NSString alloc] initWithData:data encoding:NSUTF8StringEncoding]);
     
    // 2015-09-09 22:58:58.010 01-NSURLSession基本使用[3368:81761] {"success":"登錄成功"}
    }];
    // 3.執(zhí)行task
    [task resume];
    
    • GET2: 使用URL創(chuàng)建Task
    NSURL *url = [NSURL URLWithString:@"http://120.25.226.186:32812/login?username=520it&pwd=520it&type=JSON"];
    
    // 1.session創(chuàng)建
    NSURLSession *session = [NSURLSession sharedSession];
    // 2.使用session創(chuàng)建task
    // 如果通過傳入url創(chuàng)建task,方法內(nèi)部會(huì)自動(dòng)創(chuàng)建一個(gè)request,略微省事;
    // 如果是發(fā)送get請(qǐng)求,或者不需要設(shè)置請(qǐng)求頭信息,那么建議使用當(dāng)前方法發(fā)送請(qǐng)求??
    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.執(zhí)行task
    [task resume];
    
  • POST方法

    • 關(guān)鍵在于修改請(qǐng)求體中的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.創(chuàng)建session
    NSURLSession *session = [NSURLSession sharedSession];
    // 2.根據(jù)session,創(chuàng)建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.執(zhí)行task
    [task resume];
    

1.2 NSURLSessionDownloadTask

NSURLSessionDownloadTask簡單使用

  • 注意:

    • 默認(rèn)情況下,使用NSURLSessionDownloadTask時(shí),系統(tǒng)已經(jīng)幫我們實(shí)現(xiàn)邊下載邊存儲(chǔ).防止內(nèi)存暴增.
    • 而需要我們做的只是將下載的資源從不安全的tmp文件夾挪到caches文件夾.
    NSURL *url = [NSURL URLWithString:@"http://120.25.226.186:32812/resources/images/minion_02.png"];
    NSURLRequest *request = [NSURLRequest requestWithURL:url];
    
    // NSURLSessionDownloadTask
    // 1.創(chuàng)建session
    NSURLSession *session = [NSURLSession sharedSession];
    
    // 2.根據(jù)session創(chuàng)建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);
     // 參數(shù)解釋:
     // location 即是下載好的資源在沙盒中的地址
     // NSURLSessionDownloadTask已經(jīng)默認(rèn)幫我們實(shí)現(xiàn)了,邊下載邊寫入沙盒,以防內(nèi)存暴增;
     
     // 查看默認(rèn)下載地址
     NSLog(@"%@",location);
     // 默認(rèn)是在tmp文件夾下,不安全,我們需要做的只是把它移到我們需要的位置:caches
     
     // 使用NSFileManager
     NSFileManager *manager = [NSFileManager defaultManager];
     // 將下載好的文件轉(zhuǎn)移至caches
     //  NSString *toPath = [[location lastPathComponent] cacheDir];
     NSString *toPath = [response.suggestedFilename  cacheDir];
     [manager moveItemAtURL:location toURL:[NSURL fileURLWithPath:toPath] error:nil];
    }
    // 3.執(zhí)行task
    [task resume];
    
    //==========================================
    // 通過url創(chuàng)建task
    [session downloadTaskWithURL:<#(NSURL *)#> completionHandler:<#^(NSURL *location, NSURLResponse *response, NSError *error)completionHandler#>]
    // 通過上次下載中斷中處創(chuàng)建新task-->斷點(diǎn)下載
    [session downloadTaskWithResumeData:<#(NSData *)#> completionHandler:<#^(NSURL *location, NSURLResponse *response, NSError *error)completionHandler#>];
    

NSURLSessionDownloadTask監(jiān)聽下載進(jìn)度

  • downloadTask可以暫停/取消/恢復(fù)下載
    • 這里,無論從suspended還是canle中resume,都是程序正常狀態(tài)下的;
    • 如果程序意外中斷,downloadTask提供的方法還不足以完成,dataTask可以完成!??
    • 注意: 使用代理方法時(shí),不要使用block方法處理返回?cái)?shù)據(jù).否則代理方法失效.
  • 監(jiān)聽下載進(jìn)度
- (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.創(chuàng)建session
  /*
   第一個(gè)參數(shù):Session的配置信息
   第二個(gè)參數(shù): 代理
   第三個(gè)參數(shù): 決定了代理方法在哪個(gè)線程中執(zhí)行
   */
   self.session = [NSURLSession sessionWithConfiguration:[NSURLSessionConfiguration defaultSessionConfiguration] delegate:self delegateQueue:[NSOperationQueue mainQueue]];
  
  // 2.根據(jù)session創(chuàng)建downloadTask
   self.task = [self.session downloadTaskWithURL:url];
   // 注意:這里如果使用回調(diào)方法,那么代理方法就不起作用了!!!????
  //    [session downloadTaskWithURL:<#(NSURL *)#> completionHandler:<#^(NSURL *location, NSURLResponse *response, NSError *error)completionHandler#>]
  
  // 3.執(zhí)行Task
  [self.task resume];
}

// =========================================== 
// 接收到服務(wù)器反饋的數(shù)據(jù)是調(diào)用,開始寫入數(shù)據(jù)
/*注意:該方法會(huì)調(diào)用一次或者多次
bytesWritten:當(dāng)前寫入數(shù)據(jù)量;
totalBytesWritten:總共寫入數(shù)據(jù)量;
totalBytesExpectedToWrite:服務(wù)器返回給我們的文件大小
*/
- (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;
  
}
// 寫完數(shù)據(jù)調(diào)用
- (void)URLSession:(NSURLSession *)session downloadTask:(NSURLSessionDownloadTask *)downloadTask didFinishDownloadingToURL:(NSURL *)location
{
  NSLog(@"didFinishDownloadingToURL");

  // 數(shù)據(jù)寫入完成時(shí),要把數(shù)據(jù)從tmp文件夾轉(zhuǎn)移至caches
  NSFileManager *manager = [NSFileManager defaultManager];
  NSURL *toUrl = [NSURL fileURLWithPath:self.path];
  [manager moveItemAtURL:location toURL:toUrl error:nil];

}
// 下載完成
// 如果調(diào)用該方法時(shí),error有值,表示下載出現(xiàn)錯(cuò)誤
- (void)URLSession:(NSURLSession *)session task:(NSURLSessionTask *)task didCompleteWithError:(NSError *)error
{
  NSLog(@"didCompleteWithError");
  
}

// 恢復(fù)下載時(shí)調(diào)用
- (void)URLSession:(NSURLSession *)session downloadTask:(NSURLSessionDownloadTask *)downloadTask didResumeAtOffset:(int64_t)fileOffset expectedTotalBytes:(int64_t)expectedTotalBytes
{
  NSLog(@"didResumeAtOffset");
}

// ===================取消/恢復(fù)=================== 
/**暫停*/
- (IBAction)pause:(id)sender {
  [self.task suspend];
}

/**  從暫停中恢復(fù)*/
- (IBAction)pause2goon:(id)sender {
  
  [self.task resume];
}

/**取消 */
- (IBAction)cance:(id)sender {
    // 注意這里如果使用這樣的取消,那么就沒辦法恢復(fù)了!??
  //  [self.task cancel];

  // 如果是調(diào)用cancelByProducingResumeData方法, 方法內(nèi)部會(huì)回調(diào)一個(gè)block, 在block中會(huì)將resumeData傳遞給我們
  // resumeData中就保存了當(dāng)前下載任務(wù)的配置信息(下載到什么地方, 從什么地方恢復(fù)等等)??
  [self.task cancelByProducingResumeData:^(NSData *resumeData) {
      self.resumeData = resumeData;
  }];
}

/** 從取消中恢復(fù)*/
- (IBAction)cance2goon:(id)sender {
  
  // 從上次中斷數(shù)據(jù)處新建下載任務(wù)
  self.task = [self.session downloadTaskWithResumeData:self.resumeData];
  [self.task resume];
}

1.3 NSURLSessionDataTask

NSURLSessionDataTask代理方法

  • 注意:NSURLSessionDataTask的代理方法中,默認(rèn)情況下是不接受服務(wù)器返回的數(shù)據(jù)的.如果想接受服務(wù)器返回的數(shù)據(jù),必須手動(dòng)告訴系統(tǒng),我們需要接收數(shù)據(jù)??
- (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.創(chuàng)建session
   NSURLSession *session = [NSURLSession sessionWithConfiguration:[NSURLSessionConfiguration defaultSessionConfiguration] delegate:self delegateQueue:[NSOperationQueue mainQueue]];
   // 2.根據(jù)session創(chuàng)建Task
   NSURLSessionDataTask *task = [session dataTaskWithRequest:request];
   
   // 3.執(zhí)行Task
   [task resume];
}

// ===================代理方法====================
#pragma mark - NSURLSessionDataDelegate
// 服務(wù)器響應(yīng)時(shí)調(diào)用
- (void)URLSession:(NSURLSession *)session dataTask:(NSURLSessionDataTask *)dataTask didReceiveResponse:(NSURLResponse *)response completionHandler:(void (^)(NSURLSessionResponseDisposition))completionHandler
{
   NSLog(@"didReceiveResponse");
   // 參數(shù)解釋:??
   // 系統(tǒng)默認(rèn)是不會(huì)去調(diào)用didReceiveData和didCompleteWithError,必須手動(dòng)告訴系統(tǒng),我們需要接收數(shù)據(jù)
   /* 可見:NSURLSessionResponseDisposition默認(rèn)== 0 ,也就是說默認(rèn)是不接收數(shù)據(jù)的??
    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);
}

// 收到服務(wù)器返回的數(shù)據(jù)時(shí)調(diào)用
- (void)URLSession:(NSURLSession *)session dataTask:(NSURLSessionDataTask *)dataTask didReceiveData:(NSData *)data
{
   NSLog(@"didReceiveData");
}

// 請(qǐng)求完畢時(shí)調(diào)用,如果error有值,代表請(qǐng)求失敗
- (void)URLSession:(NSURLSession *)session task:(NSURLSessionTask *)task didCompleteWithError:(NSError *)error
{
   NSLog(@"didCompleteWithError");
}

NSURLSessionDataTask斷點(diǎn)下載

  • 說明:
    • 由于dataTask并不擅長下載任務(wù),所以如果用其完成斷點(diǎn)下載,那么還是得自己使用文件句柄,或者輸出流完成! 同時(shí)在request中設(shè)置下載位置.
    • 顯然這并不是好的,但是使用擅長下載的downloadTask,貌似我們目前又很難從實(shí)現(xiàn)意外中斷恢復(fù)繼續(xù)下載;
  • 注意: 這里是使用DataTask完成下載, 并不是專業(yè)的,無法從cancel重恢復(fù).
- (void)viewDidLoad {
   [super viewDidLoad];
   // 初始化操作
   // 1.初始化路徑
   self.path = [@"" cacheDir];
   // 2.初始化currentLength
   self.currentLength = [self fileDataSize:self.path];
}

// ==============================================
#pragma mark - lazy
- (NSURLSession *)session
{
  if (!_session) {
      // 1.創(chuàng)建session
      _session = [NSURLSession sessionWithConfiguration:[NSURLSessionConfiguration defaultSessionConfiguration] delegate:self delegateQueue:[NSOperationQueue mainQueue]];
  }
  
  return _session;
}

- (NSURLSessionDataTask *)task
{
  if (!_task) {
      //如果想實(shí)現(xiàn)意外中斷后的繼續(xù)下載,NSURLSessionDataTask也需要通過設(shè)置請(qǐng)求頭的Range來實(shí)現(xiàn)????
      NSURL *url = [NSURL URLWithString:@"http://120.25.226.186:32812/resources/videos/minion_02.mp4"];
      NSMutableURLRequest *request = [NSMutableURLRequest requestWithURL:url];
      
      // 設(shè)置請(qǐng)求頭??????
      NSString *range = [NSString stringWithFormat:@"bytes:%zd-",[self fileDataSize:self.path]];
      [request setValue:range forHTTPHeaderField:@"Range"];
      
      // 2.根據(jù)session創(chuàng)建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
// 服務(wù)器響應(yīng)時(shí)調(diào)用
- (void)URLSession:(NSURLSession *)session dataTask:(NSURLSessionDataTask *)dataTask didReceiveResponse:(NSURLResponse *)response completionHandler:(void (^)(NSURLSessionResponseDisposition))completionHandler
{
  completionHandler(NSURLSessionResponseAllow);

  // 數(shù)據(jù)總大小
  self.totalLength = response.expectedContentLength + [self fileDataSize:self.path];
  // 感覺 self.currentLength = [self fileDataSize:self.path]??可以試試
}
// 收到服務(wù)器返回的數(shù)據(jù)時(shí)調(diào)用,調(diào)用一次或者多次
- (void)URLSession:(NSURLSession *)session dataTask:(NSURLSessionDataTask *)dataTask didReceiveData:(NSData *)data
{
  // 輸出流寫入數(shù)據(jù)
  [self.outputStream write:data.bytes maxLength:data.length];
  
  // 計(jì)算進(jìn)度
  self.currentLength += data.length;
  self.progressView.progress = 1.0 * self.currentLength / self.totalLength;
}

// 請(qǐng)求完畢時(shí)調(diào)用,如果error有值,代表請(qǐng)求失敗,由于沒有didFinish方法,所以一旦完成下載,就會(huì)掉該方法,和downloadTask不同!??
- (void)URLSession:(NSURLSession *)session task:(NSURLSessionTask *)task didCompleteWithError:(NSError *)error
{
  // 關(guān)閉輸出流
  [self.outputStream close];
}

1.4.NSURLSessionUploadTask

1.4.1.文件上傳

  • 說明:

    • 和NSURLConnection中的文件上傳區(qū)別在于:
      • 設(shè)置請(qǐng)求體時(shí),不可以request.HTTPBody = bodyData ; 需要將bodyData設(shè)置到創(chuàng)建的task中.
    // 設(shè)置請(qǐng)求頭
    request.HTTPMethod = @"POST";
    [request setValue:[NSString stringWithFormat:@"multipart/form-data; boundary=%@", XMGBoundary] forHTTPHeaderField:@"Content-Type"];
    
    // 設(shè)置請(qǐng)求體
    NSMutableData *body = [NSMutableData data];
    // 文件參數(shù)
    // 分割線
    [body appendData:XMGEncode(@"--")];
    [body appendData:XMGEncode(XMGBoundary)];
    [body appendData:XMGNewLine];
    .....
    
    // 注意這里通過設(shè)置請(qǐng)求體 = data完成文件上傳,官方說這樣做會(huì)被忽略
    // 就是說, 如果利用NSURLSessionUploadTask上傳文件, 那么請(qǐng)求體必須寫在fromData參數(shù)中, 不能設(shè)置在request中. 否則設(shè)置在request中會(huì)被忽略
    /*
    The body stream and body data in this request object are ignored.
    */
    // request.HTTPBody = data; ?
    
    // 1.創(chuàng)建session
    NSURLSession *session = [NSURLSession sharedSession];
    
    // 2.根據(jù)session創(chuàng)建Task
    //注意這里的data是文件參數(shù)和非文件參數(shù)的拼接二進(jìn)制??
    NSURLSessionUploadTask *task = [session uploadTaskWithRequest:request fromData: body completionHandler:^(NSData *data, NSURLResponse *response, NSError *error) {
       NSLog(@"%@",[[NSString alloc] initWithData:data encoding:NSUTF8StringEncoding]);
    }];
    
    // 3.執(zhí)行Task
    [task resume];
    
    // 注意:不要使用這個(gè)方法. fromFile方法是用于PUT請(qǐng)求上傳文件的
      // 而我們的服務(wù)器只支持POST請(qǐng)求上傳文件 
    [session uploadTaskWithRequest:request fromFile:fileUrl completionHandler:^(NSData *data, NSURLResponse *response, NSError *error) {}];?
    

1.4.2.文件上傳的監(jiān)聽

    - 關(guān)鍵: 設(shè)置代理及實(shí)現(xiàn)對(duì)應(yīng)代理方法即可
    - 核心代碼:
........
// 1.創(chuàng)建session
 NSURLSession *session = [NSURLSession sessionWithConfiguration:[NSURLSessionConfiguration defaultSessionConfiguration] delegate:self delegateQueue:[NSOperationQueue mainQueue]];
 
 // 2.根據(jù)session創(chuàng)建Task
 // 該方法中有回調(diào)函數(shù),會(huì)影響代理方法調(diào)用??
 // NSURLSessionUploadTask *task = [session uploadTaskWithRequest:request fromData:body completionHandler:^(NSData *data, NSURLResponse *response, NSError *error) { }];
 
 NSURLSessionUploadTask *task = [session uploadTaskWithRequest:request fromData:body];
 
 // 3.執(zhí)行Task
 [task resume];

// =================代理方法====================
#pragma mark - NSURLSessionTaskDelegate
// 上傳過程中調(diào)用
/*
bytesSent:當(dāng)前這一次上傳的數(shù)據(jù)大小;
totalBytesSent:總共上傳數(shù)據(jù)大小
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);
}

// 請(qǐng)求完畢時(shí)調(diào)用
- (void)URLSession:(NSURLSession *)session task:(NSURLSessionTask *)task didCompleteWithError:(NSError *)error
{ 
   NSLog(@"didCompleteWithError"); 
}

2.AFN

2.1 NSURLConnection的封裝

  • 關(guān)鍵: 拿到AFHTTPRequestOperationManager 對(duì)象
  • get 方法
// 1.創(chuàng)建manager
AFHTTPRequestOperationManager *manager = [AFHTTPRequestOperationManager manager];

// 2.使用manager發(fā)送get任務(wù)
/*
NSString:需要請(qǐng)求的url地址字符串
parameters: 請(qǐng)求是需要傳遞的參數(shù),需要以字典形式
success:請(qǐng)求成功時(shí)回調(diào)的函數(shù)
failure:請(qǐng)求失敗時(shí)的回調(diào)函數(shù)
*/
// 注意AFNetworking的get方法發(fā)送請(qǐng)求時(shí),參數(shù)和資源地址是分開寫的!??
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:
    這里默認(rèn)服務(wù)器返回給我們的數(shù)據(jù)是JSON數(shù)據(jù),然后會(huì)自動(dòng)把數(shù)據(jù)轉(zhuǎn)換為OC對(duì)象;
    如果真實(shí)返回類型不是JSON,那么默認(rèn)情況下不會(huì)回調(diào)success block,直接回調(diào)failure block
    */
   NSLog(@"%@",responseObject); 
   /*服務(wù)器返回?cái)?shù)據(jù)是JSON數(shù)據(jù)時(shí),打印如下:
    2015-09-09 14:58:41.587 08-ANF基本使用[3605:115247] {
    success = "\U767b\U5f55\U6210\U529f";
    }
    */
} failure:^(AFHTTPRequestOperation *operation, NSError *error){
   NSLog(@"error");
   /*當(dāng)服務(wù)器返回?cái)?shù)據(jù)類型不是JSON時(shí),直接回調(diào)failure函數(shù)
    2015-09-09 15:00:14.309 08-ANF基本使用[3685:116977] error
    */
}];
  • post 方法
// 1.創(chuàng)建requestOperationManager
AFHTTPRequestOperationManager *manager = [AFHTTPRequestOperationManager manager];

// 2.使用manager發(fā)送post請(qǐng)求
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的封裝

  • 關(guān)鍵: 拿到AFHTTPSessionManager 對(duì)象
  • get 方法
// 1.創(chuàng)建manager
AFHTTPSessionManager *manager = [AFHTTPSessionManager manager];

// 2.利用manager發(fā)送get請(qǐng)求
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.創(chuàng)建manager
AFHTTPSessionManager *manager = [AFHTTPSessionManager manager];
// 2.利用manager發(fā)送post請(qǐng)求
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.創(chuàng)建manager
    AFHTTPSessionManager *manager = [AFHTTPSessionManager manager];
    
    // 2.使用manager創(chuàng)建下載任務(wù)
    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) {
        // 請(qǐng)求完成的回調(diào)
        // targetPath:文件默認(rèn)下載后保存的路徑
        // response:響應(yīng)頭
        // NSURL:block返回值,告訴AFN框架,是否需要將下載的文件轉(zhuǎn)移到其他地方
        NSString *path = [response.suggestedFilename cacheDir];
        return [NSURL fileURLWithPath:path];
        
    } completionHandler:^(NSURLResponse *response, NSURL *filePath, NSError *error) {
        // 下載完成的回調(diào)
        // filePath:移動(dòng)之后的文件路徑
        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.恢復(fù)下載
    [task resume];
    
  • 監(jiān)聽下載進(jìn)度

    • 關(guān)鍵: 需要使用KVO 進(jìn)行屬性監(jiān)聽.

      • 主代碼

    -(void)monitorDownloadProgress
    {
    // 1.創(chuàng)建sessionManager
    AFHTTPSessionManager *manager = [AFHTTPSessionManager manager];
    // 2.使用sessionManager創(chuàng)建task
    NSURLRequest *request = [NSURLRequest requestWithURL:[NSURL URLWithString:@"http://120.25.226.186:32812/resources/videos/minion_02.mp4"]];

    NSProgress *progress = nil;
    self.progress = progress;
    //說明,這里任務(wù)被加入線程循環(huán)中,然后根據(jù)progress的地址,不斷根據(jù)下載進(jìn)度不斷更新progress,所以我們才可以監(jiān)聽進(jìn)度??
    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);
    }];
    // 上述方法只會(huì)被調(diào)用一次,無法監(jiān)控progress,只能使用屬性和代理方法??;
    // 不過可惜,這里無法設(shè)置代理,只能通過KVO,通過KVO,那么這里我們就無需定義屬性了!!!??
    // 那么如何通過監(jiān)控progress,拿到下載進(jìn)度那?經(jīng)查勘了解到,progress有兩個(gè)屬性
    /
    NSProgress只是一個(gè)對(duì)象!如何跟蹤進(jìn)度!-> KVO 對(duì)屬性變化的監(jiān)聽!
    @property int64_t totalUnitCount: 需要下載的文件的總大小
    @property int64_t completedUnitCount:已經(jīng)下載的文件大小
    */
    // 讓self監(jiān)控progress的屬性變化
    [progress addObserver:self forKeyPath:@"completedUnitCount" options:NSKeyValueObservingOptionNew context:nil];

    // 3.task resume
    [task resume];
    /*結(jié)果如下
    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
      {
        // 根據(jù)開發(fā)經(jīng)驗(yàn),由于一個(gè)project中可能存在諸多被observed對(duì)象,所以在coding時(shí),一定記得判斷object類型??????????
       // 與跳轉(zhuǎn)相似
       if ([object isKindOfClass:[NSProgress class]]) {
           NSProgress *p = (NSProgress *)object;
           // 下載進(jìn)度
           NSLog(@"%f",1.0 * p.completedUnitCount / p.totalUnitCount);
        // 獲得準(zhǔn)確進(jìn)度
        /**
         準(zhǔn)確的獲得進(jìn)度
         localizedDescription               10%
         localizedAdditionalDescription     completed 32,768 of 318,829
         fractionCompleted         0.102776(completedUnitCount/totalUnitCount)
         */
          NSLog(@"%@, %@, %f", p.localizedDescription, p.localizedAdditionalDescription, p.fractionCompleted);
       }
      }   
      ```
    
    -  勿忘移除監(jiān)聽??
          
      ```objc
       -(void)dealloc
          {
             [self.progress removeObserver:self forKeyPath:@"completedUnitCount"];
          }
      ```
    

2.4 upload

  • 注意: AFNetworking 框架上傳數(shù)據(jù)時(shí),不是使用uploadTask ,如果這個(gè),還得拼接上傳請(qǐng)求體格式????
  • 我們使用AFN中的POST: constructingBodyWith: 方法
- (void)upload
{
// 1.創(chuàng)建manager
AFHTTPSessionManager *manager = [AFHTTPSessionManager manager];

// 2.利用manager post文件
NSString *path = @"http://120.25.226.186:32812/upload";
NSDictionary *para = @{
                           @"username":@"pj"
                          };
/*
參數(shù)說明:
參數(shù)一:上傳服務(wù)器地址;
參數(shù)二:非文件參數(shù);
formData:用來存儲(chǔ)需要用來上傳的文件二進(jìn)制數(shù)據(jù)
*/
[manager POST:path parameters:para constructingBodyWithBlock:^(id<AFMultipartFormData> formData) {
   
   // 在這個(gè)block中上傳文件數(shù)據(jù)
   // formData就是專門用于保存需要上傳文件的二進(jìn)制數(shù)據(jù)的
   // formData如何存儲(chǔ)數(shù)據(jù)?三個(gè)方法:
   
   // 1.appendPartWithFileData:
   /*參數(shù)說明
    NSData: 需要上傳的文件二進(jìn)制數(shù)據(jù)
    name: 上傳服務(wù)器字段(服務(wù)器對(duì)應(yīng)的參數(shù)名稱)
    fileName:服務(wù)器上保持該文件的名稱
    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] 成功回調(diào){
    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] 成功回調(diào){
    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] 成功回調(diào){
    success = "\U4e0a\U4f20\U6210\U529f";
    }
    */
   //---------------------------------------------------
   // 4.注意該方法不是用來上傳數(shù)據(jù)的??
   // [formData appendPartWithFormData:<#(NSData *)#> name:<#(NSString *)#>]
   // } success:^(NSURLSessionDataTask *task, id responseObject) {
   //} failure:^(NSURLSessionDataTask *task, NSError *error) {
   //}];

  // 這個(gè)方法也不行,還得拼接二進(jìn)制data,太麻煩了!!??
//  NSURLSessionUploadTask *task = [manager uploadTaskWithRequest:request fromData:<#(NSData *)#> progress:<#(NSProgress *__autoreleasing *)#> completionHandler:<#^(NSURLResponse *response, id responseObject, NSError *error)completionHandler#>];
  // 這個(gè)是PUT請(qǐng)求,post請(qǐng)求不能使用??
  // [manager uploadTaskWithRequest:<#(NSURLRequest *)#> fromFile:<#(NSURL *)#> progress:<#(NSProgress *__autoreleasing *)#> completionHandler:<#^(NSURLResponse *response, id responseObject, NSError *error)completionHandler#>];
}

2.5 序列化

  • JSON數(shù)據(jù)
- (void)serializerJSON
{
   // 1.創(chuàng)建manager
   AFHTTPSessionManager *manager = [AFHTTPSessionManager manager];
   
   // 如果現(xiàn)實(shí)不出來,加上下面兩句話?? 一般情況不會(huì)解析不出來,因?yàn)锳F框架默認(rèn)就是解析JSON數(shù)據(jù).
   manager.responseSerializer = [AFJSONResponseSerializer serializer];
   [AFJSONResponseSerializer serializer].acceptableContentTypes = [NSSet setWithObject:@"text/json"];
   
   
   // 2.根據(jù)manager執(zhí)行post login請(qǐng)求
   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框架自動(dòng)轉(zhuǎn)換成字典對(duì)象了
       2015-09-09 18:02:24.647 08-ANF基本使用[5863:209466] {
       success = "\U767b\U5f55\U6210\U529f";
       }
       */
       // 如果上面字典中type 不是 JSON , 那么直接進(jìn)入failure!除非提前說明.
   } failure:^(NSURLSessionDataTask *task, NSError *error) {
       NSLog(@"error");
   }];
}
  • XML數(shù)據(jù)
    • 如果服務(wù)器返回的數(shù)據(jù)不是JSON,我們?nèi)绾翁崆巴ㄖ狝FN框架?
    • 如果提前告知AFN框架服務(wù)器返回?cái)?shù)據(jù)是XML類型,那么框架就會(huì)將返回一個(gè)解析器對(duì)象(也作出了處理)??
- (void)serializerXML
{
// 1.創(chuàng)建manager
AFHTTPSessionManager *manager = [AFHTTPSessionManager manager];

// 告訴AFN框架,服務(wù)器返回?cái)?shù)據(jù)類型
// 1.1 AFN將服務(wù)器返回?cái)?shù)據(jù)看做是XML類型,不做處理
manager.responseSerializer = [AFXMLParserResponseSerializer serializer];
// 如果還出錯(cuò),加下面這句話??
[AFXMLParserResponseSerializer serializer].acceptableContentTypes = [NSSet setWithObject:@"text/xml"];;

// 2.根據(jù)manager執(zhí)行post login請(qǐng)求
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) {
    
    //只要設(shè)置AFN的responseSerializer為XML, 那么返回的responseObject就是NSXMLParser解析器,而不是數(shù)據(jù)對(duì)象了??
    NSLog(@"%@",responseObject);
    /*
     08-ANF基本使用[6150:229599] <NSXMLParser: 0x7fa95ccc9920>
     */
    // 這里可以再解析
    // NSXMLParser *parser = (NSXMLParser *)responseObject;
    // parser.delegate = self;
    // [parser parse];
    
} failure:^(NSURLSessionDataTask *task, NSError *error) {
    NSLog(@"error");
}];
}
  • 二進(jìn)制數(shù)據(jù)
    • 如果提前告知AFN框架服務(wù)器返回?cái)?shù)據(jù)是二進(jìn)制類型,也就是說不做任何處理??
- (void)serializer
{
   // 1.創(chuàng)建manager
   AFHTTPSessionManager *manager = [AFHTTPSessionManager manager];
   
   // 告訴AFN框架,服務(wù)器返回?cái)?shù)據(jù)類型
   // 1.2 AFN將服務(wù)器返回?cái)?shù)據(jù)看做是二進(jìn)制類型,也就是說不做任何處理??
   manager.responseSerializer = [AFHTTPResponseSerializer serializer];
   
   // 2.根據(jù)manager執(zhí)行post login請(qǐng)求
   // 測試下載
   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);
       /* 結(jié)果:
        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
    • 優(yōu)點(diǎn): 替換框架只需要求改單例類即可
      • 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, ^{
        // 一般來說,為了重構(gòu),我們不這樣寫!??
//        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之后,使用手動(dòng)導(dǎo)入AFNetworking,會(huì)有問題:
    • 不使用Cocoapods時(shí),post下載總是不成功的!!

3.網(wǎng)絡(luò)監(jiān)測

3.1 蘋果官方做法

//蘋果自家的網(wǎng)絡(luò)監(jiān)控
#import "Reachability.h"

- (void)viewDidLoad {
    [super viewDidLoad];
    
     // 1.創(chuàng)建reachability對(duì)象(蜂窩網(wǎng)/局域網(wǎng)都行)
    self.network = [Reachability reachabilityForLocalWiFi];
    
    // 2.讓self通過通知中心監(jiān)聽reachability狀態(tài)變化
    [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(getNetworkStatus) name:kReachabilityChangedNotification object:nil];
    
    // 3.reachability開始發(fā)通知
    [self.network startNotifier];
}
- (void)dealloc
{
    [[NSNotificationCenter defaultCenter] removeObserver:self];
}
- (void)getNetworkStatus
{
    // 判斷蜂窩網(wǎng)是否可得
    if ([Reachability reachabilityForInternetConnection].currentReachabilityStatus != NotReachable) {
        NSLog(@"當(dāng)前為蜂窩網(wǎng)");
    }else if([Reachability reachabilityForLocalWiFi].currentReachabilityStatus != NotReachable){  // 判斷局域網(wǎng)是否可得
        NSLog(@"當(dāng)前為局域網(wǎng)");
    }else{
        NSLog(@"沒有網(wǎng)絡(luò)");
    }
}

3.2 AFNetworking

- (void)AFMonitorNetwork
{
    // 首先看看AFN框架如何做到監(jiān)控網(wǎng)絡(luò)狀態(tài)
    // 1.創(chuàng)建網(wǎng)絡(luò)監(jiān)聽管理者
    // 單例!
    AFNetworkReachabilityManager *manager = [AFNetworkReachabilityManager sharedManager];
    //AFNetworkReachabilityManager *manager1 = [AFNetworkReachabilityManager sharedManager];
    //NSLog(@"%@\n%@",manager,manager1);
    /*
     <AFNetworkReachabilityManager: 0x7ff5618ab3a0>
     <AFNetworkReachabilityManager: 0x7ff5618ab3a0>
     */
    
    // 2.設(shè)置網(wǎng)絡(luò)變化時(shí)的回調(diào)block
    /*
     AFNetworkReachabilityStatusUnknown          = 不能識(shí)別,
     AFNetworkReachabilityStatusNotReachable     = 沒有網(wǎng)絡(luò),
     AFNetworkReachabilityStatusReachableViaWWAN = 蜂窩網(wǎng),
     AFNetworkReachabilityStatusReachableViaWiFi = 局域網(wǎng),
     */
    [manager setReachabilityStatusChangeBlock:^(AFNetworkReachabilityStatus status) {
        switch (status) {
            case AFNetworkReachabilityStatusReachableViaWWAN:
                NSLog(@"蜂窩網(wǎng)");
                break;
                
            case AFNetworkReachabilityStatusReachableViaWiFi:
                NSLog(@"局域網(wǎng)");
                break;
            case AFNetworkReachabilityStatusNotReachable:
                NSLog(@"沒有網(wǎng)");
                break;
                
            default:
                NSLog(@"不能識(shí)別");
                break;
        }
    }];
    
    // 3.開始監(jiān)聽:這樣就持續(xù)不斷的監(jiān)聽了.....o(╯□╰)o
    [manager startMonitoring];
    /*2015-09-09 22:16:44.966 10-網(wǎng)絡(luò)監(jiān)測[2658:55183] 局域網(wǎng)*/
}
最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請(qǐng)結(jié)合常識(shí)與多方信息審慎甄別。
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡書系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容