YTKNetwork的理解

优势功能

相比 AFNetworking,YTKNetwork 提供了以下更高级的功能:

1.支持按时间缓存网络请求内容
2.支持按版本号缓存网络请求内容
3.支持统一设置服务器和 CDN 的地址
4.支持检查返回 JSON 内容的合法性
5.支持文件的断点续传
6.支持 block 和 delegate 两种模式的回调方式
7.支持批量的网络请求发送,并统一设置它们的回调(实现在 YTKBatchRequest 类中)
支持方便地设置有相互依赖的网络请求的发送,例如:发送请求 A,根据请求 A 的结果,选择性的发送请求 B
和 C,再根据 B 和 C 的结果,选择性的发送请求 D。(实现在 YTKChainRequest 类中)
支持网络请求 URL 的 filter,可以统一为网络请求加上一些参数,或者修改一些路径。

基本思想

YTKNetwork 的基本的思想是把每一个网络请求封装成对象。所以使用 YTKNetwork,你的每一个请求都需要继承 YTKRequest 类,通过覆盖父类的一些方法来构造指定的网络请求。
把每一个网络请求封装成对象其实是使用了设计模式中的 Command 模式,它有以下好处:

1.将网络请求与具体的第三方库依赖隔离,方便以后更换底层的网络库
2.方便在基类中处理公共逻辑
3.方便在基类中处理缓存逻辑,以及其它一些公共逻辑。
4.方便做对象的持久化。
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 ^(id formData) {
                    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];
    }
}

你可能感兴趣的:(YTKNetwork的理解)