美文网首页
小尝YTKNetwork

小尝YTKNetwork

作者: Bo_cake | 来源:发表于2017-01-17 14:12 被阅读0次

    一、什么是YTKNetwork

    YTKNetwork是一个基于AFNetworking的网络层封装。

    二、包括那几个类

    ○  YTKBaseRequest

    ○  YTKRequest

    ○  YTKNetworkAgent

    ○  YTKNetworkConfig

    △YTKNetwork的基本用法:基本用法,YTKNetwork 的基本的思想是把每一个网络请求封装成对象。所以使用 YTKNetwork,你的每一种请求都需要继承 YTKRequest类,通过覆盖父类的一些方法来构造指定的网络请求。

    下面会分别讲解这几个类。

    YTKNetworkAgent

    网络请求的总代理类,是对AFNetworking的封装。此类是一个单例。

    内部包含的三个成员变量:

    AFHTTPRequestOperationManager *_manager;

    AFHTTPRequestOperationManagerd的单例网路请求manager对象

    YTKNetworkConfig *_config;

    负责配置一个相关的设置

    NSMutableDictionary *_requestsRecord;

    请求队列

    对外的接口有以下方法:

    -(void)addRequest:(YTKBaseRequest *)request;

    // 这里详细分析一下addRequest的内部实现

    - (void)addRequest:(YTKBaseRequest *)request {

    YTKRequestMethod method = [request requestMethod];

    NSString *url = [self buildRequestUrl:request];

    id param = request.requestArgument;

    AFConstructingBlock constructingBlock = [request constructingBodyBlock];

    // 设置返回对象的格式,YTKRequestSerializerTypeHTTP代表返回二进制格式,YTKRequestSerializerTypeJSON代表返回一个json的根对象(NSDictionary或者NSArray)

    if (request.requestSerializerType == YTKRequestSerializerTypeHTTP) {

    _manager.requestSerializer = [AFHTTPRequestSerializer serializer];

    } else if (request.requestSerializerType == YTKRequestSerializerTypeJSON) {

    _manager.requestSerializer = [AFJSONRequestSerializer serializer];

    }

    _manager.requestSerializer.timeoutInterval = [request requestTimeoutInterval];

    // 如果请求需要授权证书,这里设置用户名和密码

    NSArray *authorizationHeaderFieldArray = [request requestAuthorizationHeaderFieldArray];

    if (authorizationHeaderFieldArray != nil) {

    [_manager.requestSerializer setAuthorizationHeaderFieldWithUsername:(NSString *)authorizationHeaderFieldArray.firstObject

    password:(NSString *)authorizationHeaderFieldArray.lastObject];

    }

    // 设置其他HTTP header

    NSDictionary *headerFieldValueDictionary = [request requestHeaderFieldValueDictionary];

    if (headerFieldValueDictionary != nil) {

    for (id httpHeaderField in headerFieldValueDictionary.allKeys) {

    id value = headerFieldValueDictionary[httpHeaderField];

    if ([httpHeaderField isKindOfClass:[NSString class]] && [value isKindOfClass:[NSString class]]) {

    [_manager.requestSerializer setValue:(NSString *)value forHTTPHeaderField:(NSString *)httpHeaderField];

    } else {

    YTKLog(@"Error, class of key/value in headerFieldValueDictionary should be NSString.");

    }}}

    // 如果创建了自定义的NSURLRequest对象,就使用自定的对象

    NSURLRequest *customUrlRequest= [request buildCustomUrlRequest];

    if (customUrlRequest) {

    // 创建 AFHTTPRequestOperation 对象

    AFHTTPRequestOperation *operation = [[AFHTTPRequestOperation alloc] initWithRequest:customUrlRequest];

    [operation setCompletionBlockWithSuccess:^(AFHTTPRequestOperation *op, id responseObject) {

    // 处理结果

    [self handleRequestResult:op];

    } failure:^(AFHTTPRequestOperation *op, NSError *error) {

    [self handleRequestResult:op];

    }];

    request.requestOperation = operation;

    operation.responseSerializer = _manager.responseSerializer;

    // 添加到请求队列

    [_manager.operationQueue addOperation:operation];

    } else {

    // 没有自定义NSURLRequest,需要手动创建

    if (method == YTKRequestMethodGet) {

    // 如果需要断点续传下载文件

    if (request.resumableDownloadPath) {

    // 拼接参数到url

    NSString *filteredUrl = [YTKNetworkPrivate urlStringWithOriginUrlString:url appendParameters:param];

    NSURLRequest *requestUrl = [NSURLRequest requestWithURL:[NSURL URLWithString:filteredUrl]];

    AFDownloadRequestOperation *operation = [[AFDownloadRequestOperation alloc] initWithRequest:requestUrl targetPath:request.resumableDownloadPath shouldResume:YES];

    // 设置断点续传的进度回调block

    [operation setProgressiveDownloadProgressBlock:request.resumableDownloadProgressBlock];

    // 整个请求完成的回调block

    [operation setCompletionBlockWithSuccess:^(AFHTTPRequestOperation *op, id responseObject) {

    [self handleRequestResult:op];

    } failure:^(AFHTTPRequestOperation *op, NSError *error) {

    [self handleRequestResult:op];

    }];

    request.requestOperation = operation;

    [_manager.operationQueue addOperation:operation];

    } else {

    request.requestOperation = [_manager GET:url parameters:param success:^(AFHTTPRequestOperation *operation, id responseObject) {

    [self handleRequestResult:operation];

    } failure:^(AFHTTPRequestOperation *operation, NSError *error) {

    [self handleRequestResult:operation];

    }];

    }

    } else if (method == YTKRequestMethodPost) {

    if (constructingBlock != nil) {

    // constructingBlock是一个返回实现AFMultipartFormData协议的对象,该对象主要作用是实现文件上传

    // 我们通常会上传图片或者文件需要用到multipart/form-data,实现以下即可:                /*                  - (AFConstructingBlock)constructingBodyBlock {                  return ^(idformData) {

    NSData *data = UIImageJPEGRepresentation([UIImage imageNamed:@"currentPageDot"], 0.9);

    NSString *name = @"image";

    NSString *formKey = @"image";

    NSString *type = @"image/jpeg";

    [formData appendPartWithFileData:data name:formKey fileName:name mimeType:type];

    };

    }*/

    request.requestOperation = [_manager POST:url parameters:param constructingBodyWithBlock:constructingBlock success:^(AFHTTPRequestOperation *operation, id responseObject) {

    [self handleRequestResult:operation];

    } failure:^(AFHTTPRequestOperation *operation, NSError *error) {

    [self handleRequestResult:operation];

    }];

    } else {

    request.requestOperation = [_manager POST:url parameters:param success:^(AFHTTPRequestOperation *operation, id responseObject) {

    [self handleRequestResult:operation];

    } failure:^(AFHTTPRequestOperation *operation, NSError *error) {

    [self handleRequestResult:operation];

    }];

    }

    } else if (method == YTKRequestMethodHead) {

    // 只返回head的请求

    request.requestOperation = [_manager HEAD:url parameters:param success:^(AFHTTPRequestOperation *operation) {

    [self handleRequestResult:operation];

    } failure:^(AFHTTPRequestOperation *operation, NSError *error) {

    [self handleRequestResult:operation];

    }];

    } else if (method == YTKRequestMethodPut) {

    // 更新资源的请求

    request.requestOperation = [_manager PUT:url parameters:param success:^(AFHTTPRequestOperation *operation, id responseObject) {

    [self handleRequestResult:operation];

    } failure:^(AFHTTPRequestOperation *operation, NSError *error) {

    [self handleRequestResult:operation];

    }];

    } else if (method == YTKRequestMethodDelete) {

    // 删除资源

    request.requestOperation = [_manager DELETE:url parameters:param success:^(AFHTTPRequestOperation *operation, id responseObject) {

    [self handleRequestResult:operation];

    } failure:^(AFHTTPRequestOperation *operation, NSError *error) {

    [self handleRequestResult:operation];

    }];

    } else if (method == YTKRequestMethodPatch) {

    // 对PUT请求的补充,更新部分资源

    request.requestOperation = [_manager PATCH:url parameters:param success:^(AFHTTPRequestOperation *operation, id responseObject) {

    [self handleRequestResult:operation];

    } failure:^(AFHTTPRequestOperation *operation, NSError *error) {

    [self handleRequestResult:operation];

    }];

    } else {

    YTKLog(@"Error, unsupport method type");

    return;}}

    // 添加一个请求到_requestsRecord字典中,key是AFHTTPRequestOperation的hash值,value是YTKBaseRequest对象

    // _requestsRecord的作用:当请求完成时,AFN返回operation,通过_requestsRecord可以反射出它所属的YTKBaseRequest对象

    [self addOperation:request];

    }

    -(void)cancelRequest:(YTKBaseRequest *)request;

    - (void)cancelRequest:(YTKBaseRequest *)request {

    [request.requestOperation cancel];

    [self removeOperation:request.requestOperation];

    [request clearCompletionBlock];

    }

    -(void)cancelAllRequests;

    - (void)cancelAllRequests {

    NSDictionary *copyRecord = [_requestsRecord copy];

    for (NSString *key in copyRecord) {

    YTKBaseRequest *request = copyRecord[key];

    [request stop];

    }

    }

    -(NSString )buildRequestUrl:(YTKBaseRequest )request;

    YTKBaseRequest

    YTKRequest的父类,先介绍它几个重要的属性和方法。

    需要子类来重写的方法

    /// 请求成功的回调

    -(void)requestCompleteFilter;

    /// 请求失败的回调

    -(void)requestFailedFilter;

    /// 请求的URL

    -(NSString *)requestUrl;

    /// 请求的CdnURL

    -(NSString *)cdnUrl;

    /// 请求的BaseURL

    (NSString *)baseUrl;

    /// 请求的连接超时时间,默认为60秒

    -(NSTimeInterval)requestTimeoutInterval;

    /// 请求的参数列表

    -(id)requestArgument;

    /// 用于在cache结果,计算cache文件名时,忽略掉一些指定的参数

    -(id)cacheFileNameFilterForRequestArgument:(id)argument;

    /// Http请求的方法

    -(YTKRequestMethod)requestMethod;

    /// 请求的SerializerType

    -(YTKRequestSerializerType)requestSerializerType;

    /// 请求的Server用户名和密码

    -(NSArray *)requestAuthorizationHeaderFieldArray;

    /// 在HTTP报头添加的自定义参数    

      -(NSDictionary *)requestHeaderFieldValueDictionary;

        /// 构建自定义的UrlRequest,

      /// 若这个方法返回非nil对象,会忽略requestUrl, requestArgument, requestMethod, requestSerializerType        -(NSURLRequest *)buildCustomUrlRequest;  

    /// 是否使用CDN的host地址        -(BOOL)useCDN;    /// 用于检查JSON是否合法的对象        -(id)jsonValidator;  

    /// 用于检查Status Code是否正常的方法        -(BOOL)statusCodeValidator;    /// 当POST的内容带有文件等富文本时使用        -(AFConstructingBlock)constructingBodyBlock; 

      /// 当需要断点续传时,指定续传的地址        -(NSString*)resumableDownloadPath; 

      /// 当需要断点续传时,获得下载进度的回调        -(AFDownloadProgressBlock)resumableDownloadProgressBlock;里面重要的两个方法:- (void)start {   

    // 调用即将开始请求的hook  

    [self toggleAccessoriesWillStartCallBack];    [[YTKNetworkAgent sharedInstance] addRequest:self];}

    /// remove self from request queue- (void)stop { 

      // 即将结束的hook 

      [self toggleAccessoriesWillStopCallBack];  

    self.delegate = nil; 

      [[YTKNetworkAgent sharedInstance] cancelRequest:self];  

    [self toggleAccessoriesDidStopCallBack];}

    还有一个比较重要的增加hook的方法,需要自定义个对象,实现YTKRequestAccessory协议定义的一些方法来hook一些动作- (void)addAccessory:(id)accessory {

    // 因为可能有多个hook对象,所以添加到一个数组中,调用的时候也是遍历数组调用

    if (!self.requestAccessories) {

    self.requestAccessories = [NSMutableArray array];

    }

    [self.requestAccessories addObject:accessory];

    }

    YTKRequest

    这里主要实现了一些缓存策略,重写了父类的start方法

    - (void)start {

    if (self.ignoreCache) {

    [super start];

    return;

    }

    // 查看缓存时间是否过期

    if ([self cacheTimeInSeconds] < 0) {

    [super start];

    return;

    }

    // 查看本地的缓存版本号和当前缓存判断是否匹配

    long long cacheVersionFileContent = [self cacheVersionFileContent];

    if (cacheVersionFileContent != [self cacheVersion]) {

    [super start];

    return;

    }

    // 查看缓存文件是否存在

    NSString *path = [self cacheFilePath];

    NSFileManager *fileManager = [NSFileManager defaultManager];

    if (![fileManager fileExistsAtPath:path isDirectory:nil]) {

    [super start];

    return;

    }

    // 查看缓存时间是否过期

    int seconds = [self cacheFileDuration:path];

    if (seconds < 0 || seconds > [self cacheTimeInSeconds]) {

    [super start];

    return;

    }

    // 加载缓存数据

    _cacheJson = [NSKeyedUnarchiver unarchiveObjectWithFile:path];

    if (_cacheJson == nil) {

    [super start];

    return;

    }

    _dataFromCache = YES;

    // 调用缓存结束回调

    [self requestCompleteFilter];

    YTKRequest *strongSelf = self;

    [strongSelf.delegate requestFinished:strongSelf];

    if (strongSelf.successCompletionBlock) {

    strongSelf.successCompletionBlock(strongSelf);

    }

    [strongSelf clearCompletionBlock];

    }

    缓存是存放在本地文件中的,文件名用一些关键字的字符串拼接并md5来表示:

    - (NSString *)cacheFileName {

    NSString *requestUrl = [self requestUrl];

    NSString *baseUrl = [YTKNetworkConfig sharedInstance].baseUrl;

    id argument = [self cacheFileNameFilterForRequestArgument:[self requestArgument]];

    NSString *requestInfo = [NSString stringWithFormat:@"Method:%ld Host:%@ Url:%@ Argument:%@ AppVersion:%@ Sensitive:%@", (long)[self requestMethod], baseUrl, requestUrl,

    argument, [YTKNetworkPrivate appVersionString], [self cacheSensitiveData]];

    NSString *cacheFileName = [YTKNetworkPrivate md5StringFromString:requestInfo];

    return cacheFileName;

    }

    YTKNetworkConfig

    这个类主要负责一些配置的工作,配置baseUrl,cdnUrl等工作,内部没有什么具体的实现,在其他类中获取这个类的配置信息

    YTKBatchRequestAgent、YTKBatchRequest

    用于方便地发送批量的网络请求,YTKBatchRequest是一个容器类,它可以放置多个 YTKRequest 子类,并统一处理这多个网络请求的成功和失败。

    在如下的示例中,我们发送了4个批量的请求,并统一处理这4个请求同时成功的回调。

    - (void)sendBatchRequest {

    GetImageApi *a = [[GetImageApi alloc] initWithImageId:@"1.jpg"];

    GetImageApi *b = [[GetImageApi alloc] initWithImageId:@"2.jpg"];

    GetImageApi *c = [[GetImageApi alloc] initWithImageId:@"3.jpg"];

    GetUserInfoApi *d = [[GetUserInfoApi alloc] initWithUserId:@"123"];

    YTKBatchRequest *batchRequest = [[YTKBatchRequest alloc] initWithRequestArray:@[a, b, c, d]];

    [batchRequest startWithCompletionBlockWithSuccess:^(YTKBatchRequest *batchRequest) {

    NSLog(@"succeed");

    NSArray *requests = batchRequest.requestArray;

    GetImageApi *a = (GetImageApi *)requests[0];

    GetImageApi *b = (GetImageApi *)requests[1];

    GetImageApi *c = (GetImageApi *)requests[2];

    GetUserInfoApi *user = (GetUserInfoApi *)requests[3];

    // deal with requests result ...

    } failure:^(YTKBatchRequest *batchRequest) {

    NSLog(@"failed");

    }];

    }

    内部实现,start方法遍历所有request,并调用start方法

    - (void)start {

    if (_finishedCount > 0) {

    YTKLog(@"Error! Batch request has already started.");

    return;

    }

    [[YTKBatchRequestAgent sharedInstance] addBatchRequest:self];

    [self toggleAccessoriesWillStartCallBack];

    for (YTKRequest * req in _requestArray) {

    req.delegate = self;

    [req start];

    }

    }

    在成功回调中,有一个计数器,判断所有请求是否都已经完成

    - (void)requestFinished:(YTKRequest *)request {

    _finishedCount++;

    if (_finishedCount == _requestArray.count) {

    [self toggleAccessoriesWillStopCallBack];

    if ([_delegate respondsToSelector:@selector(batchRequestFinished:)]) {

    [_delegate batchRequestFinished:self];

    }

    if (_successCompletionBlock) {

    _successCompletionBlock(self);

    }

    [self clearCompletionBlock];

    [self toggleAccessoriesDidStopCallBack];

    }

    }

    YTKChainRequestAgent、YTKChainRequest

    用于管理有相互依赖的网络请求,它实际上最终可以用来管理多个拓扑排序后的网络请求。

    以下是具体的代码示例,在示例中,我们在sendChainRequest方法中设置好了Api相互的依赖,然后。 我们就可以通过chainRequestFinished回调来处理所有网络请求都发送成功的逻辑了。如果有任何其中一个网络请求失败了,则会触发chainRequestFailed回调。

    - (void)sendChainRequest {

    RegisterApi *reg = [[RegisterApi alloc] initWithUsername:@"username" password:@"password"];

    YTKChainRequest *chainReq = [[YTKChainRequest alloc] init];

    [chainReq addRequest:reg callback:^(YTKChainRequest *chainRequest, YTKBaseRequest *baseRequest) {

    RegisterApi *result = (RegisterApi *)baseRequest;

    NSString *userId = [result userId];

    GetUserInfoApi *api = [[GetUserInfoApi alloc] initWithUserId:userId];

    [chainRequest addRequest:api callback:nil];

    }];

    chainReq.delegate = self;

    // start to send request

    [chainReq start];

    }

    - (void)chainRequestFinished:(YTKChainRequest *)chainRequest {

    // all requests are done

    }

    - (void)chainRequestFailed:(YTKChainRequest *)chainRequest failedBaseRequest:(YTKBaseRequest*)request {

    // some one of request is failed

    }

    内部实现,定义一个_nextRequestIndex,初始化为0,_requestArray请求数组,_requestCallbackArray请求回调数组

    - (void)start {

    if (_nextRequestIndex > 0) {

    YTKLog(@"Error! Chain request has already started.");

    return;

    }

    if ([_requestArray count] > 0) {

    [self toggleAccessoriesWillStartCallBack];

    [self startNextRequest];

    [[YTKChainRequestAgent sharedInstance] addChainRequest:self];

    } else {

    YTKLog(@"Error! Chain request array is empty.");

    }}

    // 顺序执行请求,_nextRequestIndex++

    - (BOOL)startNextRequest {

    if (_nextRequestIndex < [_requestArray count]) {

    YTKBaseRequest *request = _requestArray[_nextRequestIndex];

    _nextRequestIndex++;

    request.delegate = self;

    [request start];

    return YES;

    } else {

    return NO;}}

    // 请求成功回调

    - (void)requestFinished:(YTKBaseRequest *)request {

    // 获取当前请求的回调,并调用其回调,回调中需要用户自己去再次去add一个新的request

    NSUInteger currentRequestIndex = _nextRequestIndex - 1;

    ChainCallback callback = _requestCallbackArray[currentRequestIndex];

    callback(self, request);

    // 当不能继续执行请求时,结束本次chain请求,调用完成回调

    if (![self startNextRequest]) {

    [self toggleAccessoriesWillStopCallBack];

    if ([_delegate respondsToSelector:@selector(chainRequestFinished:)]) {

    [_delegate chainRequestFinished:self];

    [[YTKChainRequestAgent sharedInstance] removeChainRequest:self];

    }

    [self toggleAccessoriesDidStopCallBack];}}

    YTKNetworkPrivate

    定义一些内部使用的工具方法

    相关文章

      网友评论

          本文标题:小尝YTKNetwork

          本文链接:https://www.haomeiwen.com/subject/ftmmbttx.html