1. 程式人生 > >iOS網路通訊類庫用AFNetworking替換ASHttpRequest的解決方案

iOS網路通訊類庫用AFNetworking替換ASHttpRequest的解決方案

一、AFNetworking替換ASHttpRequest原因
經過近兩年開發IOS使用AFNetworking和ASIHttpRequest的經驗,結合在實際開發App的遇到網路問題

和線上app使用者反饋及在用測試工具開發測試中的反應情況,最後得出結論AFNetworking的優點相對ASIHttpReqeuest的優點大一些,尤其在網路穩定性方面。處理crash這些情況,下面通過兩者詳細的優缺點比較瞭解 一下,為什麼把ASIHttpRequest給替換成了ASNetworking。

二、AFNetworking的優缺點
優點:
        維護和使用者相對多
        對ARC的支援
        對Block使用的支援非常好


        維護和使用者相對多(目前已到2.4.1版)
       專門對非同步請求的支援
       支援基於url快取的支援
       圖片及NSURLCache、AFCache快取實用
       支援後臺下載,快取離線請求的支援(SDURLCache)
       對JSON支援好
      對實現的http請求比較容易
      對https的支援

缺點:
      是基於Foundation框架的NSURLConnection對網路進行的封裝不是用的低層的
CFNetwork進行的封裝擴充套件的功能目前相對少一些,但在Http上的網路支援已足夠

ASIHttpRequest:

優點:
對網路底層CFNetwork支援,在效能上佔優
        對代理proxy支援設定比較方便
        支援離線下載和基於url的快取
        對https的支援
        對實現的http請求方面實現的不是太好,會多寫點程式碼
        支援非同步和同步的支援


缺點:
       在2012年社群已停止更新
       對目前最新版本IOS7和8上使用時會有些問題,特別是網路非同步請求多時,多執行緒使用時經常出現時容易出現網路不穩定的問題。
       對ARC支援不好,不支援蘋果推薦給我們使用的ARC效能
       對Block不夠好,框架程式碼在多執行緒方面用的不好


       ASI框架在面向物件方面不夠抽象
       介面和實現程式碼看著過於龐大,裡面過程化的實現比較嚴重不利於擴充套件和維護

三、在程式碼實現僅用GET請求來做一下AFNetworking和ASIHttpRequest的比較:

/** 
 * AFNetworking初始化HTTP 
 */  
- (void)httpInit  
{  
    //應用配置檔案  
    self.manager = [AFHTTPRequestOperationManager manager];  
      
    //申明返回的結果是JSON型別  
    self.manager.responseSerializer = [AFJSONResponseSerializer serializer];  
      
    //如果報接受型別不一致請替換一致text/html  
    self.manager.responseSerializer.acceptableContentTypes = [NSSet setWithObject:@"text/html"];  
      
    //清求時間設定  
    self.manager.requestSerializer.timeoutInterval = 30;  
      
    //設定代理  
    //[self setProxy];  
      
    //新增header頭資訊  
    [self addRequestHeader];  
}  
  
/** 
 * AFNetworking傳送get請求 
 */  
- (void)startGet:(NSString*)uri tag:(NSInteger)tag  
{  
    [self httpInit];  
      
    [self.manager GET:[NSString stringWithFormat:@"%@%@",self.baseUrl,uri] parameters:nil success:^(AFHTTPRequestOperation *operation, id responseObject) {  
          
        [self requestFinished:responseObject tag:tag];  
    } failure:^(AFHTTPRequestOperation *operation, NSError *error) {  
  
        [self requestFailed:tag];  
    }];  
}  

  
/** 
 * ASIHttpRequest初始化HTTP 
 */  
-(void)httpInit:(NSString *)method uri:(NSString *)uri  
{  
    //應用配置檔案  
    [self setRes:[ASIFormDataRequest requestWithURL:[NSURL URLWithString:[NSString stringWithFormat:@"%@%@",self.baseUrl,uri]]]];  
  
    //設定代理  
    if (self.host != nil) {  
        [self.res setProxyHost:self.host];  
    }  
      
    if (self.port > 0) {  
        [self.res setProxyPort:self.port];  
    }  
    //設定代理  
    [self.res setDelegate:self];  
      
    //設定允許記錄cookie  
    [self.res setUseCookiePersistence:YES];  
      
    //超時設定  
    [self.res setTimeOutSeconds:30];  
      
    //請求方式  
    [self.res setRequestMethod:method];  
}  
  
/** 
 * ASIHttpRequest傳送get請求 
 */  
- (void)startGet:(NSString*)uri tag:(NSInteger)tag{  
  
    [self cancel];//取消前一個請求  
    [self httpInit:@"GET" uri:uri];  
    [self.res setTag:tag];  
    [self.res startAsynchronous];  
}  

四、AFNetworking在Model層對app的網路請求的封裝實現過的程式碼:
initData.h
//
//  InitData.h
//
//  Created by ligangming on 14-1-2.
//  Copyright (c) 2014年 ligangming. All rights reserved.
//

#define DEFAULT_PAGESIZE 20 //預設分頁數量

/**
 請求完成處理
*/
typedef void (^Complete)();

/**
 請求失敗處理
*/
typedef void (^Failed)(NSString *state,NSString *errmsg);

/**
資料請求模型的基類,包含基本網路請求
*/
@interface InitData : NSObject{

@protected

    Complete _complete; //請求完成
    Failed   _failed;   //請求失敗
}

//HTTP引數設定
@property(nonatomic,strong)NSString         *baseUrl;       //API基礎地址
@property(nonatomic,strong)NSString         *host;          //代理主機IP地址
@property(nonatomic,assign)NSInteger        port;           //代理主機埠

@property(nonatomic,strong)NSString         *errCode;       //錯誤程式碼
@property(nonatomic,strong)NSString         *errMsg;        //錯誤描述
@property(nonatomic,strong)NSString         *version;       //協議版本(客戶端相容最小版本)

- (id)initWithDelegate:(id)delegate;
/**
 * 傳送get請求
 */
- (void)startGet:(NSString*)url tag:(NSInteger)tag;

/**
 * 傳送getCache請求
 */
- (void)startCache:(NSString *)aCacheName cacheTime:(NSInteger)aTime uri:(NSString *)uri tag:(NSInteger)tag;

/**
 * 傳送post請求
 */
- (void)startPost:(NSString*)url params:(NSDictionary*)params tag:(NSInteger)tag;

/**
 * 上傳檔案
 */
- (void)uploadFileURI:(NSString*)aUri filePath:(NSString*)aPath keyName:(NSString *)aKeyName;

/**
 * 取消請求
 */
- (void)cancel;
@end

#pragma mark delegate
@protocol InitDataDelegate <NSObject>
@optional
/**
 請求完成時-呼叫
 */
-(void)getFinished:(NSDictionary *)msg tag:(NSInteger)tag;

/**
 請求失敗時-呼叫
 */
-(void)getError:(NSDictionary *)msg tag:(NSInteger)tag;

@end
initData.m
//
//  InitData.m
//
//  Created by ligangming on 14-1-2.
//  Copyright (c) 2014年 ligangming. All rights reserved.
//

#import "InitData.h"
#import "AFNetworking.h"

Class object_getClass(id object);

@interface InitData()
{
    Class afOrinClass;
}

@property(nonatomic,strong)AFHTTPRequestOperationManager *manager; //AF請求物件
@property(nonatomic,weak)id<InitDataDelegate> delegate;
@end

@implementation InitData

#pragma mark initData
- (id)initWithDelegate:(id)delegate
{
    if ((self = [super init])) {
        afOrinClass = object_getClass(delegate);
        [self setDelegate:delegate];
        [self setDefault];
    }
    
    return self;
}

/**
 * 初始化設定
 */
- (void)setDefault
{
    //載入系統配置
    NSDictionary *cfgDic = [NSObject JzbGetConfig];
    
    //API地址
    [self setBaseUrl:[cfgDic objectForKey:@"apiUrl"]];
    
    //代理HOST
    if (![[cfgDic objectForKey:@"apiHost"] isEqualToString:@""]) {
        
        [self setHost:[cfgDic objectForKey:@"apiHost"]];
        
        //重置API地址(測試用)
        [self setBaseUrl:[NSString stringWithFormat:@"http://%@", self.host]];
    }else{
        [self setHost:nil];
    }
 
    //代理port
    [self setPort:[[cfgDic objectForKey:@"apiPort"] intValue]];
}

#pragma mark httpMode
/**
 * 初始化HTTP
 */
- (void)httpInit
{
    //應用配置檔案
    self.manager = [AFHTTPRequestOperationManager manager];
    
    //申明返回的結果是JSON型別
    self.manager.responseSerializer = [AFJSONResponseSerializer serializer];
    
    //如果報接受型別不一致請替換一致text/html
    self.manager.responseSerializer.acceptableContentTypes = [NSSet setWithObject:@"text/html"];
    
    //清求時間設定
    self.manager.requestSerializer.timeoutInterval = 30;
    
    //設定代理
    //[self setProxy];
    
    //新增header頭資訊
    [self addRequestHeader];
}

/**
 * 新增header頭資訊
 */
- (void)addRequestHeader
{
    //當前應用版塊號
    
    //應用型別IOS
    
    //當前應用
    [self.manager.requestSerializer setValue:@"xxx" forHTTPHeaderField:@"TOKEN"];
}

/**
 * 傳送get請求
 */
- (void)startGet:(NSString*)uri tag:(NSInteger)tag
{
    [self httpInit];
    
    [self.manager GET:[NSString stringWithFormat:@"%@%@",self.baseUrl,uri] parameters:nil success:^(AFHTTPRequestOperation *operation, id responseObject) {
        
        [self requestFinished:responseObject tag:tag];
    } failure:^(AFHTTPRequestOperation *operation, NSError *error) {

        [self requestFailed:tag];
    }];
}

- (AFHTTPRequestOperation *)cacheOperationWithRequest:(NSURLRequest *)urlRequest
                                                  tag:(NSInteger)tag
                                              success:(void (^)(AFHTTPRequestOperation *operation, id responseObject))success
                                              failure:(void (^)(AFHTTPRequestOperation *operation, NSError *error))failure
{    
    AFHTTPRequestOperation *operation = [self.manager HTTPRequestOperationWithRequest:urlRequest success:^(AFHTTPRequestOperation *operation, id responseObject){
        
        NSCachedURLResponse *cachedURLResponse = [[NSURLCache sharedURLCache] cachedResponseForRequest:urlRequest];
        cachedURLResponse = [[NSCachedURLResponse alloc] initWithResponse:operation.response data:operation.responseData userInfo:nil storagePolicy:NSURLCacheStorageAllowed];
        [[NSURLCache sharedURLCache] storeCachedResponse:cachedURLResponse forRequest:urlRequest];
        
        success(operation,responseObject);
    }failure:^(AFHTTPRequestOperation *operation, NSError *error) {
        
        if (error.code == kCFURLErrorNotConnectedToInternet) {
            NSCachedURLResponse *cachedResponse = [[NSURLCache sharedURLCache] cachedResponseForRequest:urlRequest];
            if (cachedResponse != nil && [[cachedResponse data] length] > 0) {
                success(operation, cachedResponse.data);
            } else {
                failure(operation, error);
            }
        } else {
            failure(operation, error);
        }
    }];
    
    return operation;
}

/**
 * 傳送getCache請求
 */
- (void)startCache:(NSString *)aCacheName cacheTime:(NSInteger)aTime uri:(NSString *)uri tag:(NSInteger)tag
{
    [self httpInit];
    
    AFHTTPRequestSerializer *serializer = [AFHTTPRequestSerializer serializer];
    NSMutableURLRequest *request = [serializer requestWithMethod:@"GET" URLString:[NSString stringWithFormat:@"%@%@",self.baseUrl,uri] parameters:nil error:nil];
    
    [request setTimeoutInterval:20];
    [request setCachePolicy:NSURLRequestUseProtocolCachePolicy]; //此處將NSURLRequestReturnCacheDataElseLoad替換要不然無論有無網路情況每次請求都會取本地快取資料
    
    //請求成功Block塊
    void (^requestSuccessBlock)(AFHTTPRequestOperation *operation, id responseObject) = ^(AFHTTPRequestOperation *operation, id responseObject) {
        
        [self requestFinished:responseObject tag:tag];
    };
    
    //請求失敗Block塊
    void (^requestFailureBlock)(AFHTTPRequestOperation *operation, NSError *error) = ^(AFHTTPRequestOperation *operation, NSError *error){
        
        [self requestFailed:tag];
    };
    
    //請求資料
    AFHTTPRequestOperation *operation = [self cacheOperationWithRequest:request tag:tag success:requestSuccessBlock failure:requestFailureBlock];
    [self.manager.operationQueue addOperation:operation];
}

/** 
 * 獲取快取資料
 */
- (id)cachedResponseObject:(AFHTTPRequestOperation *)operation{
    
    NSCachedURLResponse* cachedResponse = [[NSURLCache sharedURLCache] cachedResponseForRequest:operation.request];
    AFHTTPResponseSerializer* serializer = [AFJSONResponseSerializer serializer];
    id responseObject = [serializer responseObjectForResponse:cachedResponse.response data:cachedResponse.data error:nil];
    return responseObject;
}

/**
 * 傳送post請求
 */
- (void)startPost:(NSString*)uri params:(NSDictionary*)params tag:(NSInteger)tag
{
    [self httpInit];
    
    [self.manager POST:[NSString stringWithFormat:@"%@%@",self.baseUrl,uri] parameters:params success:^(AFHTTPRequestOperation *operation, id responseObject) {
        
        [self requestFinished:responseObject tag:tag];
    } failure:^(AFHTTPRequestOperation *operation, NSError *error) {
    
        [self requestFailed:tag];
    }];
}

/**
 * 上傳檔案
 */
- (void)uploadFileURI:(NSString *)aUri filePath:(NSString *)aPath keyName:(NSString *)aKeyName
{
    [self httpInit];
    
    [self.manager POST:[NSString stringWithFormat:@"%@%@",self.baseUrl,aUri]  parameters:nil constructingBodyWithBlock:^(id<AFMultipartFormData> formData) {
        
        NSData *imageData = [[NSData alloc] initWithContentsOfFile:aPath];
        
        //獲取檔案型別
        NSMutableString *filePath = [NSMutableString stringWithString:aPath];
        CFStringRef UTI = UTTypeCreatePreferredIdentifierForTag(kUTTagClassFilenameExtension, (__bridge CFStringRef)[filePath pathExtension], NULL);
        CFStringRef MIMEType = UTTypeCopyPreferredTagWithClass (UTI, kUTTagClassMIMEType);
        
        NSString *fileName = [filePath lastPathComponent];
        
        //將得到的二進位制圖片拼接到表單中 /** data,指定上傳的二進位制流;name,伺服器端所需引數名;fileName,指定檔名;mimeType,指定檔案格式 */
        [formData appendPartWithFileData:imageData name:aKeyName fileName:fileName mimeType:(__bridge NSString *)(MIMEType)];
        
    }success:^(AFHTTPRequestOperation *operation, id responseObject){
        
        [self requestFinished:responseObject tag:200];
        
    }failure:^(AFHTTPRequestOperation *operation, NSError *error) {
        
    }];    
    
    /*
     /設定上傳操作的進度
     
     [operation setUploadProgressBlock:^(NSUInteger bytesWritten, long long totalBytesWritten, long long totalBytesExpectedToWrite) {
     
        NSLog(@"Sent %lld of %lld bytes", totalBytesWritten, totalBytesExpectedToWrite);
     }];
     
     UIProgressView *progress = [[UIProgressView alloc] initWithProgressViewStyle:UIProgressViewStyleDefault];
     
     [progress setProgressWithUploadProgressOfOperation:operation animated:YES];
     
     [progress setFrame:CGRectMake(0, 130, 320, 30)];
     
     [self.view addSubview:progress];
    */
    //[operation start];
}

/**
 停止請求
 */
- (void)cancel
{
    if (_manager != nil) {
        _manager = nil;
    }
}

/**
 * 清理回撥block
 */
- (void)cleanupBlocks
{
    _complete = nil;
    _failed = nil;
}

/*
解析通用協議頭:
1.檢查協議體合法性
2.協議版本檢查,執行協議版本客戶端處理邏輯

返回值:BOOL 通過解析檢查返回YES,否則 NO
對於沒有通過檢查的協議訊息,返回客戶端協議錯誤的訊息,或者版本不支援的錯誤
*/
- (BOOL)parseHead:(NSDictionary*)msg
{
    //錯誤程式碼解析
    //錯誤描述
    //版本提取,客戶端版本規則邏輯執行
    
    return YES;
}

#pragma mark AFHTTPDelegate
/**
 * 代理-請求結束
 */
- (void)requestFinished:(NSDictionary *)aDictionary tag:(NSInteger)aTag
{
    //如果訊息頭解析成功並通過合法性檢查
    if([self parseHead:aDictionary] == YES){

        if ([self.errCode intValue] <= 0) {
            
            if ([self.delegate respondsToSelector:@selector(getFinished:tag:)]) {
                [self.delegate getFinished:aDictionary tag:aTag];
            }
        }else{
            if (_failed) {
                _failed(self.errCode,self.errMsg);
            }
            if ([self.delegate respondsToSelector:@selector(getError:tag:)]) {
                [self.delegate getError:aDictionary tag:aTag];
            }
        }
    }
}

/**
 * 代理-請求失敗
 */
- (void)requestFailed:(NSInteger)aTag
{
    if (afOrinClass != object_getClass(_delegate)) {
        NSLog(@"model已銷燬");
        return;
    }
    
    if (_failed) {
        _failed(@"1",@"request error");
    }
    
    //檢測如果有配置代理則去執行代理
    if ([self.delegate respondsToSelector:@selector(getError:tag:)]) {
        NSDictionary *dic = [NSDictionary dictionaryWithObjectsAndKeys:@"1",@"errcode",@"request error",@"errmsg", nil];
        [self.delegate getError:dic tag:aTag];
    }
}

@end