前言
至于为什么要用AFNetworking 做APP网络请求,我这里不copy 别人的优点了,至于为什么要封装,用第三方的风险都知道,万一哪天AFNetworking 的作者不跟新了,需要使用另外一款第三方网络请求,更换也更好操作一点。
所以从综合性能,开发便利 以及后期维护风险的角度考虑,对网络请求层 进行 封装是一个APP开发工作者应有的考量。
个人对AFNetworking 的偏爱,相信大多数iOS开发者 都是跟我一样的,所以这里就基于产品开发业务需求的角度,做了一个封装。
产品需求
- 提供基本的请求传入参数接口,比如请求的ViewController,请求的地址等等
- 提供基本的请求回掉,使用 block 回掉,必须的有错误回掉,请求成功的回掉,还可以根据自己实际需求 回掉 statusCode等等内容
- 判断当前网络状态,如果无网络状态,直接返回错误回掉。
- 这些方法返回值 最好为 NSURLSessionDataTask,方便对这个网络请求做相关的取消操作。
- 对于 GET 类请求,如果请求成功,增加缓存策略
基本实现
创建一个MyBaseNetWorking文件,继承NSObject即可,
引入头文件 AFNetworkActivityIndicatorManager.h 和AFNetworking.h
step 1 准备工作 网络状态的判断
我建议大家 在AppDelegate 中直接加入这个判断,并创建一个单例Model->MyAppConfigModel,记录APP的网络状态,这个会非常有用。
- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions {
/*********** AFNetworkActivityIndicatorManager 所有通过AF发送的请求, 都会在电池条上出现圆圈提示 ***********/
[AFNetworkActivityIndicatorManager sharedManager].enabled = YES;
/*********** 监听当前网络状态 *************/
[[AFNetworkReachabilityManager sharedManager] setReachabilityStatusChangeBlock:^(AFNetworkReachabilityStatus status) {
NSLog(@"当前网络状态: %@", AFStringFromNetworkReachabilityStatus(status));
}];
// 开始监测网络状态
[[AFNetworkReachabilityManager sharedManager] startMonitoring];
[self configReachabilityStatusByAFNetwork];
}
/**
监测网络类型
*/
-(void)configReachabilityStatusByAFNetwork {
AFNetworkReachabilityManager *manager = [AFNetworkReachabilityManager sharedManager];
[manager setReachabilityStatusChangeBlock:^(AFNetworkReachabilityStatus status) {
// 用单例model 记录网络状态,这样可以在APP任何地方调用这个熟悉 都可以得到当前网络状态。
[MyAppConfigModel sharedSingleton].netWorkReachabilityStatus = status;
switch (status) {
case AFNetworkReachabilityStatusUnknown:
NSLog(@"当前网络:未知");
break;
case AFNetworkReachabilityStatusNotReachable:
NSLog(@"当前网络:网络无连接\n请检查网络");
break;
case AFNetworkReachabilityStatusReachableViaWWAN:
NSLog(@"当前网络:3G|4G");
break;
case AFNetworkReachabilityStatusReachableViaWiFi:
NSLog(@"当前网络:WiFi");
break;
default:
break;
}
}];
}
step 2 缓存策略
我这里就使用非常简单的NSKeyedArchiver
写操作:
if (cache == YES) {
NSString *docPath = [NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES) firstObjectInArr];
NSString *cachePath = [docPath stringByAppendingPathComponent:task.currentRequest.URL.absoluteString.MD5];
[[NSOperationQueue new] addOperationWithBlock:^{
[NSKeyedArchiver archiveRootObject:responseObject toFile:cachePath];
}];
}
读操作:
//如果需要缓存 读取缓存
if (cache == YES) {
NSString *cachePath = [docPath stringByAppendingPathComponent:task.currentRequest.URL.absoluteString.MD5];
id responseObj = [NSKeyedUnarchiver unarchiveObjectWithFile:cachePath];
NSLog(@" 读取缓存 get 失败 url = %@ \n statusCode =%ld",url,(long)responses.statusCode);
[[NSOperationQueue mainQueue] addOperationWithBlock:^{
if (responseObj) {
!completionHandler ?: completionHandler(responseObj, nil,(long)responses.statusCode);
}else{
!completionHandler ?: completionHandler(nil, error, (long)responses.statusCode);
}
}];
}
step 3 设置AFHTTPSessionManager对象,配置统一属性
MyBaseNetWorking.m文件中
+ (AFHTTPSessionManager *)manager {
[AFNetworkActivityIndicatorManager sharedManager].enabled = YES;
AFHTTPSessionManager *manager = [AFHTTPSessionManager manager];
//默认解析模式
manager.requestSerializer = [AFHTTPRequestSerializer serializer];
manager.responseSerializer = [AFJSONResponseSerializer serializer];
//配置请求序列化
AFJSONResponseSerializer *serializer = [AFJSONResponseSerializer serializer];
[serializer setRemovesKeysWithNullValues:YES];
manager.requestSerializer.stringEncoding = NSUTF8StringEncoding;
//配置响应序列化
manager.responseSerializer.acceptableContentTypes = [NSSet setWithArray:@[@"application/json",
@"text/html",
@"text/json",
@"text/plain",
@"text/javascript",
@"text/xml",
@"image/*",
@"application/octet-stream",
@"application/zip"]];
//请求时间
manager.requestSerializer.timeoutInterval = 15.f;
//设置请求头 举例
NSString *userID = [NSString stringWithFormat:@"%@",[kUserDefaults valueForKey:@"UserID"]];
[manager.requestSerializer setValue:[kUserDefaults valueForKey:@"Authorization"] forHTTPHeaderField:@"Authorization"];
[manager.requestSerializer setValue:userID forHTTPHeaderField:@"UserID"];
return manager;
}
step 4 设置AFHTTPSessionManager对象,配置统一属性
MyBaseNetWorking.h 文件中 设计自己的 API 接口
封装 GET 请求
MyBaseNetWorking.h文件中
/**
get 网络请求
@param path 路径 不要Base路径
@param controller 网络请求所在的控制器
@param cache 是否需要缓存
@param parameters 参数
@param completionHandler 处理事件
@return 返回NSURLSessionDataTask对象
*/
+ (id)setGET:(NSString *)path controller:(UIViewController *)controller cache:(BOOL)cache parameters:(NSDictionary *)parameters completionHandler:(void(^)(id responseObj, NSError *error,NSInteger statusCode))completionHandler;
MyBaseNetWorking.m文件中实现
/******************** 实现方法 **********************/
+(id)setGET:(NSString *)path controller:(UIViewController *)controller cache:(BOOL)cache parameters:(NSDictionary *)parameters completionHandler:(void (^)(id, NSError *, NSInteger))completionHandler{
kDefineWeakSelf;
AFHTTPSessionManager *manager = [self manager];
NSString *url = [NSString stringWithFormat:@"%@%@",BaseHttpsURL,path];
NSString *docPath = [NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES) firstObjectInArr];
NSString *encodedPath = [path stringByAddingPercentEscapesUsingEncoding:NSUTF8StringEncoding];
NSURLSessionDataTask *task = nil;
// 无网络状态
if ([MyAppConfigModel sharedSingleton].netWorkReachabilityStatus == AFNetworkReachabilityStatusNotReachable) {
NSError *err = [NSError errorWithDomain:@"ErrorDomain" code:-999 userInfo:[NSDictionary dictionaryWithObject:@"网络出现错误,请检查网络连接" forKey:NSLocalizedDescriptionKey]];
!completionHandler ?: completionHandler(nil, err, -999);
return task;
}
task= [manager GET:encodedPath parameters:parameters progress:nil success:^(NSURLSessionDataTask * _Nonnull task, id _Nullable responseObject) {
NSHTTPURLResponse * responses = (NSHTTPURLResponse *)task.response;
NSLog(@" get 请求成功 statusCode = %ld \n url=%@ \n responseObject = %@ ",(long)responses.statusCode,url,responseObject);
//如果需要缓存
if (cache == YES) {
NSString *cachePath = [docPath stringByAppendingPathComponent:task.currentRequest.URL.absoluteString.MD5];
[[NSOperationQueue new] addOperationWithBlock:^{
[NSKeyedArchiver archiveRootObject:responseObject toFile:cachePath];
}];
}
!completionHandler?:completionHandler(responseObject, nil,(long)responses.statusCode);
} failure:^(NSURLSessionDataTask * _Nullable task, NSError * _Nonnull error) {
[[NSOperationQueue new] addOperationWithBlock:^{
NSHTTPURLResponse * responses = (NSHTTPURLResponse *)task.response;
//如果需要缓存 读取缓存
if (cache == YES) {
NSString *cachePath = [docPath stringByAppendingPathComponent:task.currentRequest.URL.absoluteString.MD5];
id responseObj = [NSKeyedUnarchiver unarchiveObjectWithFile:cachePath];
NSLog(@" 读取缓存 get 失败 url = %@ \n statusCode =%ld",url,(long)responses.statusCode);
[[NSOperationQueue mainQueue] addOperationWithBlock:^{
if (responseObj) {
NSLog(@" 有缓存 ");
!completionHandler ?: completionHandler(responseObj, nil,(long)responses.statusCode);
}else{
NSLog(@" 缓存 nil");
!completionHandler ?: completionHandler(nil, error, (long)responses.statusCode);
}
}];
}else{
NSLog(@" 无缓存 get 失败 url = %@ \n statusCode =%ld",url,(long)responses.statusCode);
!completionHandler ?: completionHandler(nil, error, (long)responses.statusCode);
}
}];
}];
return task;
}
封装 POST 请求
MyBaseNetWorking.h文件中
/**
post 网络请求
@param path 路径 不要Base路径
@param controller 网络请求所在的控制器
@param parameters 参数
@param completionHandler 处理事件
@return 返回NSURLSessionDataTask对象
*/
+ (id)setPOST:(NSString *)path controller:(UIViewController *)controller parameters:(NSDictionary *)parameters completionHandler:(void(^)(id responseObj, NSError *error,NSInteger statusCode))completionHandler;
MyBaseNetWorking.m文件中实现
+(id)setPOST:(NSString *)path controller:(UIViewController *)controller parameters:(NSDictionary *)parameters completionHandler:(void (^)(id, NSError *, NSInteger))completionHandler{
kDefineWeakSelf;
AFHTTPSessionManager *manager = [self manager];
NSString *url = [NSString stringWithFormat:@"%@%@",BaseHttpsURL,path];
NSURLSessionDataTask *task = nil;
if ([MyAppConfigModel sharedSingleton].netWorkReachabilityStatus == AFNetworkReachabilityStatusNotReachable) {
NSError *err = [NSError errorWithDomain:@"ErrorDomain" code:-999 userInfo:[NSDictionary dictionaryWithObject:@"网络出现错误,请检查网络连接" forKey:NSLocalizedDescriptionKey]];
!completionHandler ?: completionHandler(nil, err, -999);
return task;
}
task = [manager POST:url parameters:parameters progress:nil success:^(NSURLSessionDataTask * _Nonnull task, id _Nullable responseObject) {
NSHTTPURLResponse * responses = (NSHTTPURLResponse *)task.response;
NSLog(@" Post url =%@ statusCode =%ld \n成功 \n responseObject = %@ ",url,(long)responses.statusCode,responseObject);
!completionHandler?:completionHandler(responseObject, nil,(long)responses.statusCode);
} failure:^(NSURLSessionDataTask * _Nullable task, NSError * _Nonnull error) {
[[NSOperationQueue new] addOperationWithBlock:^{
NSHTTPURLResponse * responses = (NSHTTPURLResponse *)task.response;
!completionHandler ?: completionHandler(nil, error,responses.statusCode);
}
}];
}else{
NSLog(@" POST 失败 url = %@ statusCode= %ld \n error = %@ ",url,(long)responses.statusCode,error);
!completionHandler ?: completionHandler(nil, error, (long)responses.statusCode);
}];
}];
return task;
}
封装 取消 请求
MyBaseNetWorking.h文件
/**
取消指定网络请求任务。
@param task 指定的网络请求任务
*/
+ (void)cancleNSURLSessionTask:(NSURLSessionTask *)task;
MyBaseNetWorking.m文件中实现
+(void)cancleNSURLSessionTask:(NSURLSessionTask *)task{
[task cancel];
}
至此 AFNetworking 基本封装完毕
如何使用?
在控制器可以创建一个对象NSURLSessionDataTask *netWorkingTask
-(void)netWorking{
[self.view showNetWorkingLoadingWithInfo:@"加载中..."];
dispatch_async(dispatch_get_global_queue(0, 0), ^{
self.netWorkingTask = [MyNormalNetWorking getHistroyCarNoListController:self completionHandler:^(id responseObj, NSError *error, NSInteger statusCode) {
dispatch_async(dispatch_get_main_queue(), ^{
if (error) {
[self.view showErrorWithInfo:@"请求失败"];
NSLog(@"error = %@",error);
}else{
[self.view showDarkTextColorToastInfo:@"请求成功" userInteractionEnabled:NO];
self.textView.text = [NSString stringWithFormat:@"%@",responseObj];
}
});
}];
});
}
// 以下方法是 继承 了MyBaseNetWorking创建的一个统一管理请求的基础类MyNormalNetWorking
+ (id)getHistroyCarNoListController:(UIViewController *)controller completionHandler:(void (^)(id , NSError *, NSInteger))completionHandler{
NSString *path = @"http://192.168.20.70:8080/parkingService/userCarNo/getHistoryCarNo.action?userId=utopa&limit=6";
return [self setGET:path controller:controller cache:YES parameters:nil completionHandler:^(id responseObj, NSError *error, NSInteger statusCode) {
!completionHandler ?: completionHandler(responseObj ,error, statusCode);
}];
}
// 取消网络请求
[MyNormalNetWorking cancleNSURLSessionTask:self.netWorkingTask];