CBNetworking:源码分析

来源:互联网 发布:windows安装光盘图片 编辑:程序博客网 时间:2024/06/17 02:43

为什么要对AFNetworking进行多一层的封装

对于AFNetworking进行二次封装是很有必要的。事实上,在项目中大量使用第三方网络库是有风险的,因为网络请求的使用遍布整个应用,而一旦该对应的网络库不再更新维护,或者我们有意愿的去更换网络框架,修改将会有着巨大的难以承受的工作量。

这一个框架与其他框架有什么不同

  • 本框架基于AFNetworking3.0的版本进行封装,面向更新的版本。

  • 为网络请求的任务管理做了大量的工作,使得下载上传,或者其他使用环境下的任务管理得以更轻松的实现。

  • 反其道而行,专为Post请求作出了缓存处理,这一部分缓存处理的使用与否,由使用者自行决定。

  • 自定义Get请求的缓存策略,以时间为基准,严格把控缓存的有效性。

接口设计

请求

这里使用了新建NS_ENUM的方式来统一Post和Get接口。

/** *  请求方式 */typedef NS_ENUM(NSInteger, CBRequestType) {    /**     *  POST方式来进行请求     */    CBPOSTRequest = 1 << 0,    /**     *  GET方式来进行请求     */    CBGETRequest  = 1 << 1};
/** *  统一请求接口 * *  @param url              请求路径 *  @param params           拼接参数 *  @param httpMethod       请求方式(0为POST,1为GET) *  @param useCache         是否使用缓存 *  @param progressBlock    进度回调 *  @param successBlock     成功回调block *  @param failBlock        失败回调block * *  @return 返回的对象中可取消请求 */+ (CBURLSessionTask *)requestWithUrl:(NSString *)url                              params:(NSDictionary *)params                            useCache:(BOOL)useCache                         httpMedthod:(CBRequestType)httpMethod                       progressBlock:(CBNetWorkingProgress)progressBlock                        successBlock:(CBResponseSuccessBlock)successBlock                           failBlock:(CBResponseFailBlock)failBlock;

从上面的代码我们可以看到,参数useCache决定着你是否使用Post请求的缓存功能,而即便将其设置为NO,依然会自动开启Get请求的缓存功能。

数据解析方式

/** *  数据串行方式 */typedef NS_ENUM(NSInteger, CBSerializerType) {    /**     *  HTTP方式来进行请求或响应     */    CBHTTPSerializer = 1 << 0,    /**     *  JSON方式来进行请求或响应     */    CBJSONSerializer = 1 << 1};
/** *  更新请求或者返回数据的解析方式(0为HTTP模式,1为JSON模式) * *  @param requestType  请求数据解析方式 *  @param responseType 返回数据解析方式 */+ (void)updateRequestSerializerType:(CBSerializerType)requestType                 responseSerializer:(CBSerializerType)responseType;

上面的NS_ENUM联合上面的方法可以更改数据解析方式,更具灵活性。

文件上传,下载

/** *  文件上传接口 * *  @param url              传文件接口地址 *  @param uploadingFile    上传文件路径 *  @param progressBlock    上传进度 *  @param successBlock     成功回调 *  @param failBlock        失败回调 * *  @return 返回的对象中可取消请求 */+ (CBURLSessionTask *)uploadFileWithUrl:(NSString *)url                          uploadingFile:(NSString *)uploadingFile                          progressBlock:(CBNetWorkingProgress)progressBlock                           successBlock:(CBResponseSuccessBlock)successBlock                              failBlock:(CBResponseFailBlock)failBlock;
/** *  文件下载接口 * *  @param url           下载文件接口地址 *  @param saveToPath    存储目录 *  @param progressBlock 下载进度 *  @param successBlock  成功回调 *  @param failBlock     下载回调 * *  @return 返回的对象可取消请求 */+ (CBURLSessionTask *)downloadWithUrl:(NSString *)url                           saveToPath:(NSString *)saveToPath                        progressBlock:(CBNetWorkingProgress)progressBlock                         successBlock:(CBResponseSuccessBlock)successBlock                            failBlock:(CBResponseFailBlock)failBlock;

针对文件的下载和上传的做出专门的设计。

任务管理

由上面的方法中,我们可以看到每一个方法都返回了一个任务对象CBURLSessionTask,这个对象继承于NSURLSessionTask,如此我们可以直接的取到每次进行任务请求的对象,直接对其进行管理。但不止于此,我仍然写了以下的几个接口来更快捷更集中的对其进行管理。

/** *  取消所有请求 */+ (void)cancelAllRequest;
/** *  根据url取消请求 * *  @param url 请求url */+ (void)cancelRequestWithURL:(NSString *)url;

通过这两个方法,我们可以直接取消所有的请求,或者取消单个链接对应的请求。全局性的执行取消操作,更方便。

缓存处理

Post缓存处理

由于苹果官方的NSURLCache不支持Post请求的缓存处理,所有这一部分的缓存处理,我自己通过归档的方式来进行管理。

主要的方法如下:

+ (id)getCacheResponseWithURL:(NSString *)url                       params:(NSDictionary *)params {    id cacheData = nil;    if (url) {        NSString *directoryPath = DIRECTORYPATH;        NSString *originString = [NSString stringWithFormat:@"%@+%@",url,params];        NSString *path = [directoryPath stringByAppendingPathComponent:[self md5:originString]];        NSData *data = [[NSFileManager defaultManager] contentsAtPath:path];        if (data) {            cacheData = [NSJSONSerialization JSONObjectWithData:data options:NSJSONReadingMutableContainers error:nil];        }    }    return cacheData;}
+ (void)cacheResponseObject:(id)responseObject                    request:(NSURLRequest *)request                     params:(NSDictionary *)params {    if (request && responseObject && ![responseObject isKindOfClass:[NSNull class]]) {        NSString *directoryPath = DIRECTORYPATH;        NSError *error = nil;        if (![[NSFileManager defaultManager] fileExistsAtPath:directoryPath isDirectory:nil]) {            [[NSFileManager defaultManager] createDirectoryAtPath:directoryPath                                      withIntermediateDirectories:YES                                                       attributes:nil                                                            error:&error];        }        NSString *originString = [NSString stringWithFormat:@"%@+%@",request.URL.absoluteString,params];        NSString *path = [directoryPath stringByAppendingPathComponent:[self md5:originString]];        NSDictionary *dict = (NSDictionary *)responseObject;        NSData *data = nil;        if ([dict isKindOfClass:[NSData class]]) {            data = responseObject;        } else {            data = [NSJSONSerialization dataWithJSONObject:dict                                                   options:NSJSONWritingPrettyPrinted                                                     error:&error];        }        if (data && error == nil) {            [[NSFileManager defaultManager] createFileAtPath:path contents:data attributes:nil];        }    }}+ (NSString *)md5:(NSString *)string {    if (string == nil || [string length] == 0) {        return nil;    }    unsigned char digest[CC_MD5_DIGEST_LENGTH], i;    CC_MD5([string UTF8String], (int)[string lengthOfBytesUsingEncoding:NSUTF8StringEncoding], digest);    NSMutableString *ms = [NSMutableString string];    for (i = 0; i < CC_MD5_DIGEST_LENGTH; i++) {        [ms appendFormat:@"%02x", (int)(digest[i])];    }    return [ms copy];}

值得注意的是,每一次进行缓存,都通过HASH的方式对缓存进行了加密,从而达到唯一缓存和更加安全的目的。

Get请求的自定义

按道理,官方已经对于Get请求进行了很完善的缓存处理,为什么我还要自行处理呢?事实上,我并没有自定义这一部分的处理,我仅对于NSURLCache加多了一层封装,从而达到自定义策略的目的,主要的方法如下所示:

- (id)cachedResponseForRequest:(NSURLRequest *)request {    NSCachedURLResponse *cachedResponse = [super cachedResponseForRequest:request];    if (cachedResponse) {        NSDate *cacheDate = cachedResponse.userInfo[CBURLCacheExpirationKey];        NSDate *cacheExpirationDate = [cacheDate dateByAddingTimeInterval:CBURLCacheExpirationInterval];        if ([cacheExpirationDate compare:[NSDate date]] == NSOrderedAscending) {            [self removeCachedResponseForRequest:request];            return nil;        }    }    id responseObj = [NSJSONSerialization JSONObjectWithData:cachedResponse.data options:NSJSONReadingAllowFragments error:nil];    return responseObj;}
- (void)storeCachedResponse:(id)response               responseObjc:(id)responseObjc                 forRequest:(NSURLRequest *)request {    NSData *data = [NSJSONSerialization dataWithJSONObject:responseObjc options:NSJSONWritingPrettyPrinted error:nil];    NSMutableDictionary *userInfo = [[NSMutableDictionary alloc] init];    userInfo[CBURLCacheExpirationKey] = [NSDate date];    NSCachedURLResponse *modifiedCachedResponse = [[NSCachedURLResponse alloc] initWithResponse:response data:data userInfo:userInfo storagePolicy:0];    [super storeCachedResponse:modifiedCachedResponse forRequest:request];}

小结

更详细的代码请大家点击下载查看,谢谢大家的关注,如果可以,请点个star支持一下,谢谢。

0 0
原创粉丝点击