AFN框架使用整理

smile麗語發表於2018-01-03

1.AFN框架基本使用

  • 1 AFN內部結構 AFN結構體
    • NSURLConnection(iOS9.0廢棄了)

      • AFURLConnectionOperation
      • AFHTTPRequestOperation
      • AFHTTPRequestOperationManager(封裝了常用的 HTTP 方法)
        • 屬性
          • baseURL :AFN建議開發者針對 AFHTTPRequestOperationManager 自定義個一個單例子類,設定 baseURL, 所有的網路訪問,都只使用相對路徑即可
          • requestSerializer :請求資料格式/預設是二進位制的 HTTP
          • responseSerializer :響應的資料格式/預設是 JSON 格式
          • operationQueue
          • reachabilityManager :網路連線管理器
        • 方法
          • manager :方便建立管理器的類方法
          • HTTPRequestOperationWithRequest :在訪問伺服器時,如果要告訴伺服器一些附加資訊,都需要在 Request 中設定
          • GET
          • POST
    • NSURLSession

      • AFURLSessionManager
      • AFHTTPSessionManager(封裝了常用的 HTTP 方法)
        • GET
        • POST
        • UIKit + AFNetworking 分類
        • NSProgress :利用KVO
    • 半自動的序列化&反序列化的功能

      • AFURLRequestSerialization :請求的資料格式/預設是二進位制的
      • AFURLResponseSerialization :響應的資料格式/預設是JSON格式
    • 附加功能

      • 安全策略
        • HTTPS
        • AFSecurityPolicy
      • 網路檢測
        • 對蘋果的網路連線檢測做了一個封裝
        • AFNetworkReachabilityManager

建議: 可以學習下AFN對 UIKit 做了一些分類, 對自己能力提升是非常有幫助的

  • 2 AFN的基本使用 (1)傳送GET請求的兩種方式(POST同)
 -(void)get1 {
    //1.建立AFHTTPRequestOperationManager管理者
    //AFHTTPRequestOperationManager內部是基於NSURLConnection實現的
    AFHTTPRequestOperationManager *manager = [AFHTTPRequestOperationManager manager];

    //2.傳送請求
    /*
     http://120.25.226.186:32812/login?username=ee&pwd=ee&type=JSON
     第一個引數:NSString型別的請求路徑,AFN內部會自動將該路徑包裝為一個url並建立請求物件
     第二個引數:請求引數,以字典的方式傳遞,AFN內部會判斷當前是POST請求還是GET請求,以選擇直接拼接還是轉換為NSData放到請求體中傳遞
     第三個引數:請求成功之後回撥Block
     第四個引數:請求失敗回撥Block
     */

    NSDictionary *param = @{
                            @"username":@"",
                            @"pwd":@""
                            };

    //注意:字串中不能包含空格
    [manager GET:@"http://120.25.226.186:32812/login" parameters:param success:^(AFHTTPRequestOperation * _Nonnull operation, id  _Nonnull responseObject) {

        NSLog(@"請求成功---%@",responseObject);

    } failure:^(AFHTTPRequestOperation * _Nonnull operation, NSError * _Nonnull error) {
            NSLog(@"失敗---%@",error);
    }];
}
複製程式碼
-(void)get2 {
    //1.建立AFHTTPSessionManager管理者
    //AFHTTPSessionManager內部是基於NSURLSession實現的
    AFHTTPSessionManager *manager = [AFHTTPSessionManager manager];

    //2.傳送請求
    NSDictionary *param = @{
                            @"username":@"",
                            @"pwd":@""
                            };

    //注意:responseObject:請求成功返回的響應結果(AFN內部已經把響應體轉換為OC物件,通常是字典或陣列)
    [manager GET:@"http://120.25.226.186:32812/login" parameters:param success:^(NSURLSessionDataTask * _Nonnull task, id  _Nonnull responseObject) {
            NSLog(@"請求成功---%@",[responseObject class]);

    } failure:^(NSURLSessionDataTask * _Nonnull task, NSError * _Nonnull error) {
        NSLog(@"失敗---%@",error);
    }];
}
複製程式碼

(2)使用AFN下載檔案

-(void)download {
    //1.建立一個管理者
    AFHTTPSessionManager *manage  = [AFHTTPSessionManager manager];

    //2.下載檔案
    /*
     第一個引數:請求物件
     第二個引數:下載進度
     第三個引數:block回撥,需要返回一個url地址,用來告訴AFN下載檔案的目標地址
         targetPath:AFN內部下載檔案儲存的地址,tmp資料夾下
         response:請求的響應頭
         返回值:檔案應該剪下到什麼地方
     第四個引數:block回撥,當檔案下載完成之後呼叫
        response:響應頭
        filePath:檔案儲存在沙盒的地址 == 第三個引數中block的返回值
        error:錯誤資訊
     */

    //2.1 建立請求物件
    NSURLRequest *request = [NSURLRequest requestWithURL:[NSURL URLWithString:@"http://120.25.226.186:32812/resources/images/minion_02.png"]];

    //2.2 建立下載進度,並監聽
    NSProgress *progress = nil;

    NSURLSessionDownloadTask *downloadTask = [manage downloadTaskWithRequest:request progress:&progress destination:^NSURL * _Nonnull(NSURL * _Nonnull targetPath, NSURLResponse * _Nonnull response) {

        NSString *caches = [NSSearchPathForDirectoriesInDomains(NSCachesDirectory, NSUserDomainMask, YES) lastObject];

        //拼接檔案全路徑
        NSString *fullpath = [caches stringByAppendingPathComponent:response.suggestedFilename];
        NSURL *filePathUrl = [NSURL fileURLWithPath:fullpath];
        return filePathUrl;

    } completionHandler:^(NSURLResponse * _Nonnull response, NSURL * _Nonnull filePath, NSError * _Nonnull error) {

        NSLog(@"檔案下載完畢---%@",filePath);
    }];

    //2.3 使用KVO監聽下載進度
    [progress addObserver:self forKeyPath:@"completedUnitCount" options:NSKeyValueObservingOptionNew context:nil];

    //3.啟動任務
    [downloadTask resume];
}
複製程式碼
//獲取並計算當前檔案的下載進度
-(void)observeValueForKeyPath:(NSString *)keyPath ofObject:(NSProgress *)progress change:(NSDictionary<NSString *,id> *)change context:(void *)context
{
    NSLog(@"%zd--%zd--%f",progress.completedUnitCount,progress.totalUnitCount,1.0 * progress.completedUnitCount/progress.totalUnitCount);
}
複製程式碼

2.AFN檔案上傳 1.檔案上傳拼接資料的第一種方式

[formData appendPartWithFileData:data name:@"file" fileName:@"xxoo.png" mimeType:@"application/octet-stream"];
複製程式碼

2.檔案上傳拼接資料的第二種方式

 [formData appendPartWithFileURL:fileUrl name:@"file" fileName:@"xx.png" mimeType:@"application/octet-stream" error:nil];
複製程式碼

3.檔案上傳拼接資料的第三種方式

 [formData appendPartWithFileURL:fileUrl name:@"file" error:nil];
複製程式碼

4.【注】在資料中已經提供了一個用於檔案上傳的分類。

/檔案上傳相關的程式碼如下/

-(void)upload {
    //1.建立一個請求管理者
    AFHTTPSessionManager *manager = [AFHTTPSessionManager manager];

    //2.傳送POST請求上傳資料
    /*
     第一個引數:請求路徑:NSString型別
     第二個引數:要上傳的非檔案引數
     第三個引數:block回撥
        在該回撥中,需要利用formData拼接即將上傳的二進位制資料
     第三個引數:上傳成功的block回撥
        task:dataTask(任務)
        responseObject:伺服器返回的資料
     第四個引數:上傳失敗的block回撥
        error:錯誤資訊,如果上傳檔案失敗,那麼error裡面包含了錯誤的描述資訊
     */

    NSDictionary *dict = @{
                           @"username":@""
                           };

    [manager POST:@"http://120.25.226.186:32812/upload" parameters:dict constructingBodyWithBlock:^(id<AFMultipartFormData>  _Nonnull formData) {

        //把本地的圖片轉換為NSData型別的資料
        UIImage *image = [UIImage imageNamed:@"123"];
        NSData *data = UIImagePNGRepresentation(image);

        /*
         //拼接二進位制檔案資料
         第一個引數:要上傳的檔案的二進位制資料
         第二個引數:伺服器介面規定的名稱
         第三個引數:這個引數上傳到伺服器之後用什麼名字來進行儲存
         第四個引數:上傳檔案的MIMEType型別
         */
        [formData appendPartWithFileData:data name:@"file" fileName:@"xxoo.png" mimeType:@"application/octet-stream"];

    } success:^(NSURLSessionDataTask * _Nonnull task, id  _Nonnull responseObject) {
        NSLog(@"請求成功---%@",responseObject);

    } failure:^(NSURLSessionDataTask * _Nonnull task, NSError * _Nonnull error) {
        NSLog(@"請求失敗--%@",error);
    }];
}
複製程式碼
-(void)upload2 {
    NSLog(@"%s",__func__);

    //1.建立一個請求管理者
    AFHTTPSessionManager *manager = [AFHTTPSessionManager manager];

    //2.傳送POST請求上傳資料
    /*
     第一個引數:請求路徑:NSString型別
     第二個引數:要上傳的非檔案引數
     第三個引數:block回撥
     在該回撥中,需要利用formData拼接即將上傳的二進位制資料
     第三個引數:上傳成功的block回撥
     task:dataTask(任務)
     responseObject:伺服器返回的資料
     第四個引數:上傳失敗的block回撥
     error:錯誤資訊,如果上傳檔案失敗,那麼error裡面包含了錯誤的描述資訊
     */

    NSDictionary *dict = @{
                           @"username":@""
                           };

    [manager POST:@"http://120.25.226.186:32812/upload" parameters:dict constructingBodyWithBlock:^(id<AFMultipartFormData>  _Nonnull formData) {

        //本地檔案的url
        NSURL *fileUrl = [NSURL fileURLWithPath:@"/Users/username/Desktop/KF[WTI`AQ3T`A@3R(B96D89.gif"];
        /*
         //拼接二進位制檔案資料
         第一個引數:要上傳檔案的url路徑
         第二個引數:伺服器要求的引數名稱
         第三個引數:這個檔案上傳到伺服器之後叫什麼名稱
         第四個引數:檔案的mimetype型別
         第五個引數:錯誤資訊
         */
//        [formData appendPartWithFileURL:fileUrl name:@"file" fileName:@"xx.png" mimeType:@"application/octet-stream" error:nil];

        //另外一種上傳檔案的方式
        /*
         說明:該方法和上面的方法等價,不過該方法更加簡單其內部會自動的的根據url路徑確定檔案儲存名稱,並通過內部方法獲取上傳檔案的mimetype型別
         */
        [formData appendPartWithFileURL:fileUrl name:@"file" error:nil];


    } success:^(NSURLSessionDataTask * _Nonnull task, id  _Nonnull responseObject) {
        NSLog(@"請求成功---%@",responseObject);

    } failure:^(NSURLSessionDataTask * _Nonnull task, NSError * _Nonnull error) {
        NSLog(@"請求失敗--%@",error);
    }];
}
複製程式碼

3.使用AFN進行序列化處理 /* 1.AFN它內部預設把伺服器響應的資料當做json來進行解析,所以如果伺服器返回給我的不是JSON資料那麼請求報錯,這個時候需要設定AFN對響應資訊的解析方式。AFN提供了三種解析響應資訊的方式,分別是: 1)AFXMLParserResponseSerializer----XML 2) AFHTTPResponseSerializer---------預設二進位制響應資料 3)AFJSONResponseSerializer---------JSON

2.還有一種情況就是伺服器返回給我們的資料格式不太一致(開發者工具Content-Type:text/xml),那麼這種情況也有可能請求不成功。解決方法: 1) 直接在原始碼中修改,新增相應的Content-Type 2) 拿到這個屬性,新增到它的集合中

3.相關程式碼

-(void)serialized {
    //1.建立請求管理者,內部基於NSURLSession
    AFHTTPSessionManager *manager = [AFHTTPSessionManager manager];

    /* 知識點1:設定AFN採用什麼樣的方式來解析伺服器返回的資料*/

    //如果返回的是XML,那麼告訴AFN,響應的時候使用XML的方式解析
    manager.responseSerializer = [AFXMLParserResponseSerializer serializer];

    //如果返回的就是二進位制資料,那麼採用預設二進位制的方式來解析資料
    //manager.responseSerializer = [AFHTTPResponseSerializer serializer];

    //採用JSON的方式來解析資料
    //manager.responseSerializer = [AFJSONResponseSerializer serializer];


    /*知識點2 告訴AFN,再序列化伺服器返回的資料的時候,支援此種型別
    [AFJSONResponseSerializer serializer].acceptableContentTypes = [NSSet setWithObject:@"text/xml"];

    //2.把所有的請求引數通過字典的方式來裝載,GET方法內部會自動把所有的鍵值對取出以&符號拼接並最後用?符號連線在請求路徑後面
    NSDictionary *dict = @{
                           @"username":@"223",
                           @"pwd":@"ewr",
                           @"type":@"XML"
                           };

    //3.傳送GET請求
    [manager GET:@"http://120.25.226.186:32812/login" parameters:dict success:^(NSURLSessionDataTask * _Nonnull task, id  _Nonnull responseObject) {

        //4.請求成功的回撥block
        NSLog(@"%@",[responseObject class]);
    } failure:^(NSURLSessionDataTask * _Nonnull task, NSError * _Nonnull error) {

        //5.請求失敗的回撥,可以列印error的值檢視錯誤資訊
        NSLog(@"%@",error);
    }];
}
複製程式碼

4.使用AFN來檢測網路狀態 /* 說明:可以使用AFN框架中的AFNetworkReachabilityManager來監聽網路狀態的改變,也可以利用蘋果提供的Reachability來監聽。建議在開發中直接使用AFN框架處理。 */ //使用AFN框架來檢測網路狀態的改變

-(void)AFNReachability {
    //1.建立網路監聽管理者
    AFNetworkReachabilityManager *manager = [AFNetworkReachabilityManager sharedManager];

    //2.監聽網路狀態的改變
    /*
     AFNetworkReachabilityStatusUnknown          = 未知
     AFNetworkReachabilityStatusNotReachable     = 沒有網路
     AFNetworkReachabilityStatusReachableViaWWAN = 3G
     AFNetworkReachabilityStatusReachableViaWiFi = WIFI
     */
    [manager setReachabilityStatusChangeBlock:^(AFNetworkReachabilityStatus status) {
        switch (status) {
            case AFNetworkReachabilityStatusUnknown:
                NSLog(@"未知");
                break;
            case AFNetworkReachabilityStatusNotReachable:
                NSLog(@"沒有網路");
                break;
            case AFNetworkReachabilityStatusReachableViaWWAN:
                NSLog(@"3G");
                break;
            case AFNetworkReachabilityStatusReachableViaWiFi:
                NSLog(@"WIFI");
                break;

            default:
                break;
        }
    }];

    //3.開始監聽
    [manager startMonitoring];
}
複製程式碼

//使用蘋果提供的Reachability來檢測網路狀態,如果要持續監聽網路狀態的概念,需要結合通知一起使用。

-(void)touchesBegan:(NSSet<UITouch *> *)touches withEvent:(UIEvent *)event {
    //1.註冊一個通知
    [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(networkChange) name:kReachabilityChangedNotification object:nil];

    //2.拿到一個物件,然後呼叫開始監聽方法
    Reachability *r = [Reachability reachabilityForInternetConnection];
    [r startNotifier];

    //持有該物件,不要讓該物件釋放掉
    self.r = r;
}

//當控制器釋放的時候,移除通知的監聽
-(void)dealloc
{
    [[NSNotificationCenter defaultCenter] removeObserver:self];
}

-(void)networkChange
{
    //獲取當前網路的狀態
   if ([Reachability reachabilityForInternetConnection].currentReachabilityStatus == ReachableViaWWAN)
    {
        NSLog(@"當前網路狀態為3G");
        return;
    }

    if ([Reachability reachabilityForLocalWiFi].currentReachabilityStatus == ReachableViaWiFi)
    {
        NSLog(@"當前網路狀態為wifi");
        return;
    }

    NSLog(@"當前沒有網路");
}
複製程式碼

5.AFN使用技巧 1.在開發的時候可以建立一個工具類,繼承自我們的AFN中的請求管理者,再控制器中真正發請求的程式碼使用自己封裝的工具類。 2.這樣做的優點是以後如果修改了底層依賴的框架,那麼我們修改這個工具類就可以了,而不用再一個一個的去修改。 3.該工具類一般提供一個單例方法,在該方法中會設定一個基本的請求路徑。 4.該方法通常還會提供對GET或POST請求的封裝。 5.在外面的時候通過該工具類來傳送請求 6.單例方法:

+ (instancetype)shareNetworkTools {
    static XMGNetworkTools *instance;
    static dispatch_once_t onceToken;
    dispatch_once(&onceToken, ^{
        // 注意: BaseURL中一定要以/結尾
        instance = [[self alloc] initWithBaseURL:[NSURL URLWithString:@"http://120.25.226.186:32812/"]];
    });
    return instance;
}
複製程式碼

相關文章