iOS網路通訊類庫用AFNetworking替換ASHttpRequest的解決方案
阿新 • • 發佈:2019-02-02
一、AFNetworking替換ASHttpRequest原因
經過近兩年開發IOS使用AFNetworking和ASIHttpRequest的經驗,結合在實際開發App的遇到網路問題
維護和使用者相對多(目前已到2.4.1版)
專門對非同步請求的支援
支援基於url快取的支援
圖片及NSURLCache、AFCache快取實用
支援後臺下載,快取離線請求的支援(SDURLCache)
對JSON支援好
對實現的http請求比較容易
對https的支援
缺點:
是基於Foundation框架的NSURLConnection對網路進行的封裝不是用的低層的
CFNetwork進行的封裝擴充套件的功能目前相對少一些,但在Http上的網路支援已足夠
ASI框架在面向物件方面不夠抽象
介面和實現程式碼看著過於龐大,裡面過程化的實現比較嚴重不利於擴充套件和維護
三、在程式碼實現僅用GET請求來做一下AFNetworking和ASIHttpRequest的比較:
四、AFNetworking在Model層對app的網路請求的封裝實現過的程式碼:
initData.h
經過近兩年開發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