IOS網絡請求之AFNetWorking 3.x 使用


前言:

    計划把公司的網絡請求與業務解耦,所以想着學習一下網絡請求,最近學習了NSURLSession,今天來學習一下基於NSURLSession封裝的優秀開源框架AFNetWorking 3.x,之前13年做iOS開發時用的ASIHttpRequest開源框架。

AFNetWorking

   AFNetWorking一款輕量級網絡請求開源框架,基於iOS和mac os 網絡進行擴展的高性能框架,大大降低了iOS開發工程師處理網絡請求的難度,讓iOS開發變成一件愉快的事情。

   GitHub地址:https://github.com/AFNetworking/AFNetworking

1.)AFHTTPSessionManager請求管理者

-(AFHTTPSessionManager *)sharedManager
{
    AFHTTPSessionManager *manager = [AFHTTPSessionManager manager];
    //最大請求並發任務數
    manager.operationQueue.maxConcurrentOperationCount = 5;

    // 請求格式
    // AFHTTPRequestSerializer            二進制格式
    // AFJSONRequestSerializer            JSON
    // AFPropertyListRequestSerializer    PList(是一種特殊的XML,解析起來相對容易)
    
    manager.requestSerializer = [AFHTTPRequestSerializer serializer]; // 上傳普通格式
    
    // 超時時間
    manager.requestSerializer.timeoutInterval = 30.0f;
    // 設置請求頭
    [manager.requestSerializer setValue:@"gzip" forHTTPHeaderField:@"Content-Encoding"];
    // 設置接收的Content-Type
    manager.responseSerializer.acceptableContentTypes = [[NSSet alloc] initWithObjects:@"application/xml", @"text/xml",@"text/html", @"application/json",@"text/plain",nil];
    
    // 返回格式
    // AFHTTPResponseSerializer           二進制格式
    // AFJSONResponseSerializer           JSON
    // AFXMLParserResponseSerializer      XML,只能返回XMLParser,還需要自己通過代理方法解析
    // AFXMLDocumentResponseSerializer (Mac OS X)
    // AFPropertyListResponseSerializer   PList
    // AFImageResponseSerializer          Image
    // AFCompoundResponseSerializer       組合
    
    manager.responseSerializer = [AFJSONResponseSerializer serializer];//返回格式 JSON
    //設置返回C的ontent-type
    manager.responseSerializer.acceptableContentTypes=[[NSSet alloc] initWithObjects:@"application/xml", @"text/xml",@"text/html", @"application/json",@"text/plain",nil];

    return manager;
}

2.)處理get請求

-(void)doGetRequest
{
    //創建請求地址
    NSString *url=@"http://api.nohttp.net/method";
    //構造參數
    NSDictionary *parameters=@{@"name":@"yanzhenjie",@"pwd":@"123"};
    //AFN管理者調用get請求方法
    [[self shareAFNManager] GET:url parameters:parameters progress:^(NSProgress * _Nonnull downloadProgress) {
        //返回請求返回進度
        NSLog(@"downloadProgress-->%@",downloadProgress);
    } success:^(NSURLSessionDataTask * _Nonnull task, id  _Nullable responseObject) {
        //請求成功返回數據 根據responseSerializer 返回不同的數據格式
        NSLog(@"responseObject-->%@",responseObject);
    } failure:^(NSURLSessionDataTask * _Nullable task, NSError * _Nonnull error) {
        //請求失敗
        NSLog(@"error-->%@",error);
    }];
}

3.)處理post請求

-(void)doPostRequestOfAFN
{
    //創建請求地址
    NSString *url=@"http://api.nohttp.net/postBody";
    //構造參數
    NSDictionary *parameters=@{@"name":@"yanzhenjie",@"pwd":@"123"};
    //AFN管理者調用get請求方法
    [[self shareAFNManager] POST:url parameters:parameters progress:^(NSProgress * _Nonnull uploadProgress) {
        //返回請求返回進度
        NSLog(@"downloadProgress-->%@",uploadProgress);
    } success:^(NSURLSessionDataTask * _Nonnull task, id  _Nullable responseObject) {
        //請求成功返回數據 根據responseSerializer 返回不同的數據格式
        NSLog(@"responseObject-->%@",responseObject);
    } failure:^(NSURLSessionDataTask * _Nullable task, NSError * _Nonnull error) {
        //請求失敗
        NSLog(@"error-->%@",error);
    }];
}

4.)處理文件上傳

-(void)doUploadRequest
{
    // 創建URL資源地址
    NSString *url = @"http://api.nohttp.net/upload";
    // 參數
    NSDictionary *parameters=@{@"name":@"yanzhenjie",@"pwd":@"123"};
    [[self shareAFNManager] POST:url parameters:parameters constructingBodyWithBlock:^(id<AFMultipartFormData>  _Nonnull formData) {
        NSDate* dat = [NSDate dateWithTimeIntervalSinceNow:0];
        NSTimeInterval a=[dat timeIntervalSince1970];
        NSString* fileName = [NSString stringWithFormat:@"file_%0.f.txt", a];
        
        [FileUtils writeDataToFile:fileName data:[@"upload_file_to_server" dataUsingEncoding:NSUTF8StringEncoding]];
        // 獲取數據轉換成data
        NSString *filePath =[FileUtils getFilePath:fileName];
        // 拼接數據到請求題中
        [formData appendPartWithFileURL:[NSURL fileURLWithPath:filePath] name:@"headUrl" fileName:fileName mimeType:@"application/octet-stream" error:nil];
        
    } progress:^(NSProgress * _Nonnull uploadProgress) {
        // 上傳進度
        NSLog(@"%lf",1.0 *uploadProgress.completedUnitCount / uploadProgress.totalUnitCount);
    } success:^(NSURLSessionDataTask * _Nonnull task, id  _Nullable responseObject) {
        //請求成功
        NSLog(@"請求成功:%@",responseObject);
        
    } failure:^(NSURLSessionDataTask * _Nullable task, NSError * _Nonnull error) {
        //請求失敗
        NSLog(@"請求失敗:%@",error);
    }];
}

5.)處理文件下載

-(void)doDownLoadRequest
{
    NSString *urlStr =@"http://images2015.cnblogs.com/blog/950883/201701/950883-20170105104233581-62069155.png";
    // 設置請求的URL地址
    NSURL *url = [NSURL URLWithString:urlStr];
    // 創建請求對象
    NSURLRequest *request = [NSURLRequest requestWithURL:url];
    // 下載任務
    NSURLSessionDownloadTask *task = [[self shareAFNManager] downloadTaskWithRequest:request progress:^(NSProgress * _Nonnull downloadProgress) {
        // 下載進度
        NSLog(@"當前下載進度為:%lf", 1.0 * downloadProgress.completedUnitCount / downloadProgress.totalUnitCount);
    } destination:^NSURL * _Nonnull(NSURL * _Nonnull targetPath, NSURLResponse * _Nonnull response) {
        // 下載地址
        NSLog(@"默認下載地址%@",targetPath);
        //這里模擬一個路徑 真實場景可以根據url計算出一個md5值 作為fileKey
        NSDate* dat = [NSDate dateWithTimeIntervalSinceNow:0];
        NSTimeInterval a=[dat timeIntervalSince1970];
        NSString* fileKey = [NSString stringWithFormat:@"/file_%0.f.txt", a];
        // 設置下載路徑,通過沙盒獲取緩存地址,最后返回NSURL對象
        NSString *filePath = [FileUtils getFilePath:fileKey];
        return [NSURL fileURLWithPath:filePath]; // 返回的是文件存放在本地沙盒的地址
    } completionHandler:^(NSURLResponse * _Nonnull response, NSURL * _Nullable filePath, NSError * _Nullable error) {
        // 下載完成調用的方法
        NSLog(@"filePath---%@", filePath);
        NSData *data=[NSData dataWithContentsOfURL:filePath];
        UIImage *image=[UIImage imageWithData:data];
        // 刷新界面...
        UIImageView *imageView =[[UIImageView alloc]init];
        imageView.image=image;
        [self.view addSubview:imageView];
        [imageView mas_makeConstraints:^(MASConstraintMaker *make) {
            make.center.equalTo(self.view);
            make.size.mas_equalTo(CGSizeMake(300, 300));
        }];
    }];
    //啟動下載任務
    [task resume];
}

6.)網絡狀態監聽

- (void)aFNetworkStatus{
    
    //創建網絡監測者
    AFNetworkReachabilityManager *manager = [AFNetworkReachabilityManager sharedManager];
    
    /*枚舉里面四個狀態  分別對應 未知 無網絡 數據 WiFi
     typedef NS_ENUM(NSInteger, AFNetworkReachabilityStatus) {
     AFNetworkReachabilityStatusUnknown          = -1,      未知
     AFNetworkReachabilityStatusNotReachable     = 0,       無網絡
     AFNetworkReachabilityStatusReachableViaWWAN = 1,       蜂窩數據網絡
     AFNetworkReachabilityStatusReachableViaWiFi = 2,       WiFi
     };
     */
    
    [manager setReachabilityStatusChangeBlock:^(AFNetworkReachabilityStatus status) {
        //這里是監測到網絡改變的block  可以寫成switch方便
        //在里面可以隨便寫事件
        switch (status) {
            case AFNetworkReachabilityStatusUnknown:
                NSLog(@"未知網絡狀態");
                break;
            case AFNetworkReachabilityStatusNotReachable:
                NSLog(@"無網絡");
                break;
                
            case AFNetworkReachabilityStatusReachableViaWWAN:
                NSLog(@"蜂窩數據網");
                break;
                
            case AFNetworkReachabilityStatusReachableViaWiFi:
                NSLog(@"WiFi網絡");
                break;
                
            default:
                break;
        }
        
    }] ;
    
    [manager startMonitoring];
}

AFNetWorking內存泄露

 通常情況我們一般會認為以manager結尾的都是單例模式,所以我們一般都是這樣使用AFNetWorking,如下

AFHTTPSessionManager *manager = [AFHTTPSessionManager manager];

其實我們點進去查看源碼發現並不是單例,而是每次都實例化一個AFHTTPSessionManager對象,源碼如下

+ (instancetype)manager {
    return [[[self class] alloc] initWithBaseURL:nil];
}

所以我們在使用AFNetWorking的時候要對AFHTTPSessionManager進行單例封裝

+ (AFHTTPSessionManager *)sharedManager
{
    static AFHTTPSessionManager *manager = nil;
    static dispatch_once_t predicate;
    dispatch_once(&predicate, ^{
        manager = [AFHTTPSessionManager manager];
        manager.operationQueue.maxConcurrentOperationCount = 5;
        manager.requestSerializer.timeoutInterval=30.f;
        manager.responseSerializer.acceptableContentTypes = [[NSSet alloc] initWithObjects:@"application/xml", @"text/xml",@"text/html", @"application/json",@"text/plain",nil];
        [manager.requestSerializer setValue:@"gzip" forHTTPHeaderField:@"Content-Encoding"];
        
    });
    return manager;
}

AFNetWorking關於HTTPS

 在2017年1月1日起Apple 要求開發者於年底之前為提交至 App Store 中的應用啟用 HTTPS ,以支持 iOS 9 引入的 ATS(App Transport Security)技術。但后來,apple 發布聲明宣布延長這個時限,提供給開發者更多的時間進行相關准備。目前 Apple 尚未公布新的截止日期。所以目前應對https的方案有兩種。

第一種方式:

   屏蔽調iOS ATS(App Transport Security),在pList.info文件中添加如下代碼

<key>NSAppTransportSecurity</key>  
    <dict>  
        <key>NSAllowsArbitraryLoads</key>  
        <true/>  
 </dict>  

第二種方式:

   配置https CA證書,這里采用獲取NSBundle中獲取CA證書,AFNetWorking提供了配置AFSecurityPolicy模塊

+ (AFSecurityPolicy *)customSecurityPolicy{
    //Https CA證書地址
    NSString *cerPath = [[NSBundle mainBundle] pathForResource:@"HTTPSCer" ofType:@"cer"];
    //獲取CA證書數據
    NSData *cerData = [NSData dataWithContentsOfFile:cerPath];
    //創建AFSecurityPolicy對象
    AFSecurityPolicy *security = [AFSecurityPolicy policyWithPinningMode:AFSSLPinningModeNone];
    //設置是否允許不信任的證書(證書無效、證書時間過期)通過驗證 ,默認為NO.
    security.allowInvalidCertificates = YES;
    //是否驗證域名證書的CN(common name)字段。默認值為YES。
    security.validatesDomainName = NO;
    //根據驗證模式來返回用於驗證服務器的證書
    security.pinnedCertificates = [NSSet setWithObject:cerData];
    return security;
}

然后通過設置AFHTTPSessionManager的securityPolicy屬性等於自定義的AFSecurityPolicy。

總結:

   簡單記錄一下AFNetWorking的基本使用,方便以后查找。


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM