【源碼閱讀】YTKNetwork

YTKNetwork是一個(gè)基于AFNetworking的網(wǎng)絡(luò)層封裝,包括以下幾個(gè)核心的類

  • YTKBaseRequest
  • YTKRequest
  • YTKNetworkAgent
  • YTKNetworkConfig

YTKNetwork的基本用法:基本用法,YTKNetwork 的基本的思想是把每一個(gè)網(wǎng)絡(luò)請(qǐng)求封裝成對(duì)象。所以使用 YTKNetwork,你的每一種請(qǐng)求都需要繼承 YTKRequest類,通過(guò)覆蓋父類的一些方法來(lái)構(gòu)造指定的網(wǎng)絡(luò)請(qǐng)求。

下面會(huì)分別講解這幾個(gè)類。

YTKNetworkAgent

網(wǎng)絡(luò)請(qǐng)求的總代理類,是對(duì)AFNetworking的封裝。此類是一個(gè)單例。

內(nèi)部包含的三個(gè)成員變量:

  • AFHTTPRequestOperationManager *_manager;
    • AFHTTPRequestOperationManagerd的單例網(wǎng)路請(qǐng)求manager對(duì)象
  • YTKNetworkConfig *_config;
    • 負(fù)責(zé)配置一個(gè)相關(guān)的設(shè)置
  • NSMutableDictionary *_requestsRecord;
    • 請(qǐng)求隊(duì)列

對(duì)外的接口有以下方法:

  • -(void)addRequest:(YTKBaseRequest *)request;
// 這里詳細(xì)分析一下addRequest的內(nèi)部實(shí)現(xiàn)
- (void)addRequest:(YTKBaseRequest *)request {
    YTKRequestMethod method = [request requestMethod];
    NSString *url = [self buildRequestUrl:request];
    id param = request.requestArgument;
    AFConstructingBlock constructingBlock = [request constructingBodyBlock];

    // 設(shè)置返回對(duì)象的格式,YTKRequestSerializerTypeHTTP代表返回二進(jìn)制格式,YTKRequestSerializerTypeJSON代表返回一個(gè)json的根對(duì)象(NSDictionary或者NSArray)
    if (request.requestSerializerType == YTKRequestSerializerTypeHTTP) {
        _manager.requestSerializer = [AFHTTPRequestSerializer serializer];
    } else if (request.requestSerializerType == YTKRequestSerializerTypeJSON) {
        _manager.requestSerializer = [AFJSONRequestSerializer serializer];
    }
    
    _manager.requestSerializer.timeoutInterval = [request requestTimeoutInterval];

    // 如果請(qǐng)求需要授權(quán)證書,這里設(shè)置用戶名和密碼
    NSArray *authorizationHeaderFieldArray = [request requestAuthorizationHeaderFieldArray];
    if (authorizationHeaderFieldArray != nil) {
        [_manager.requestSerializer setAuthorizationHeaderFieldWithUsername:(NSString *)authorizationHeaderFieldArray.firstObject
                                                                   password:(NSString *)authorizationHeaderFieldArray.lastObject];
    }
    
    // 設(shè)置其他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.");
            }
        }
    }

    // 如果創(chuàng)建了自定義的NSURLRequest對(duì)象,就使用自定的對(duì)象
    NSURLRequest *customUrlRequest= [request buildCustomUrlRequest];
    if (customUrlRequest) {
        // 創(chuàng)建 AFHTTPRequestOperation 對(duì)象
        AFHTTPRequestOperation *operation = [[AFHTTPRequestOperation alloc] initWithRequest:customUrlRequest];
        [operation setCompletionBlockWithSuccess:^(AFHTTPRequestOperation *op, id responseObject) {
            // 處理結(jié)果
            [self handleRequestResult:op];
        } failure:^(AFHTTPRequestOperation *op, NSError *error) {
            [self handleRequestResult:op];
        }];
        request.requestOperation = operation;
        operation.responseSerializer = _manager.responseSerializer;
        // 添加到請(qǐng)求隊(duì)列
        [_manager.operationQueue addOperation:operation];
    } else {
        // 沒(méi)有自定義NSURLRequest,需要手動(dòng)創(chuàng)建
        if (method == YTKRequestMethodGet) {
            // 如果需要斷點(diǎn)續(xù)傳下載文件
            if (request.resumableDownloadPath) {
                // 拼接參數(shù)到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];
                // 設(shè)置斷點(diǎn)續(xù)傳的進(jìn)度回調(diào)block
                [operation setProgressiveDownloadProgressBlock:request.resumableDownloadProgressBlock];
                // 整個(gè)請(qǐng)求完成的回調(diào)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是一個(gè)返回實(shí)現(xiàn)AFMultipartFormData協(xié)議的對(duì)象,該對(duì)象主要作用是實(shí)現(xiàn)文件上傳
                // 我們通常會(huì)上傳圖片或者文件需要用到multipart/form-data,實(shí)現(xiàn)以下即可:
                /* 
                 - (AFConstructingBlock)constructingBodyBlock {
                  return ^(id<AFMultipartFormData> 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的請(qǐng)求
            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) {
            // 更新資源的請(qǐng)求
            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) {
            // 對(duì)PUT請(qǐng)求的補(bǔ)充,更新部分資源
            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;
        }
    }

    // 添加一個(gè)請(qǐng)求到_requestsRecord字典中,key是AFHTTPRequestOperation的hash值,value是YTKBaseRequest對(duì)象
    // _requestsRecord的作用:當(dāng)請(qǐng)求完成時(shí),AFN返回operation,通過(guò)_requestsRecord可以反射出它所屬的YTKBaseRequest對(duì)象
    [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的父類,先介紹它幾個(gè)重要的屬性和方法。
需要子類來(lái)重寫的方法

  • /// 請(qǐng)求成功的回調(diào)

    • -(void)requestCompleteFilter;
  • /// 請(qǐng)求失敗的回調(diào)

    • -(void)requestFailedFilter;
  • /// 請(qǐng)求的URL

    • -(NSString *)requestUrl;
  • /// 請(qǐng)求的CdnURL

    • -(NSString *)cdnUrl;
  • /// 請(qǐng)求的BaseURL

  • (NSString *)baseUrl;

  • /// 請(qǐng)求的連接超時(shí)時(shí)間,默認(rèn)為60秒

    • -(NSTimeInterval)requestTimeoutInterval;
  • /// 請(qǐng)求的參數(shù)列表

    • -(id)requestArgument;
  • /// 用于在cache結(jié)果,計(jì)算cache文件名時(shí),忽略掉一些指定的參數(shù)

    • -(id)cacheFileNameFilterForRequestArgument:(id)argument;
  • /// Http請(qǐng)求的方法

    • -(YTKRequestMethod)requestMethod;
  • /// 請(qǐng)求的SerializerType

    • -(YTKRequestSerializerType)requestSerializerType;
  • /// 請(qǐng)求的Server用戶名和密碼

    • -(NSArray *)requestAuthorizationHeaderFieldArray;
  • /// 在HTTP報(bào)頭添加的自定義參數(shù)

    • -(NSDictionary *)requestHeaderFieldValueDictionary;
  • /// 構(gòu)建自定義的UrlRequest,

  • /// 若這個(gè)方法返回非nil對(duì)象,會(huì)忽略requestUrl, requestArgument, requestMethod, requestSerializerType

    • -(NSURLRequest *)buildCustomUrlRequest;
  • /// 是否使用CDN的host地址

    • -(BOOL)useCDN;
  • /// 用于檢查JSON是否合法的對(duì)象

    • -(id)jsonValidator;
  • /// 用于檢查Status Code是否正常的方法

    • -(BOOL)statusCodeValidator;
  • /// 當(dāng)POST的內(nèi)容帶有文件等富文本時(shí)使用

    • -(AFConstructingBlock)constructingBodyBlock;
  • /// 當(dāng)需要斷點(diǎn)續(xù)傳時(shí),指定續(xù)傳的地址

    • -(NSString *)resumableDownloadPath;
  • /// 當(dāng)需要斷點(diǎn)續(xù)傳時(shí),獲得下載進(jìn)度的回調(diào)

    • -(AFDownloadProgressBlock)resumableDownloadProgressBlock;

里面重要的兩個(gè)方法:

- (void)start {
    // 調(diào)用即將開始請(qǐng)求的hook
    [self toggleAccessoriesWillStartCallBack];
    [[YTKNetworkAgent sharedInstance] addRequest:self];
}

/// remove self from request queue
- (void)stop {
    // 即將結(jié)束的hook
    [self toggleAccessoriesWillStopCallBack];
    self.delegate = nil;
    [[YTKNetworkAgent sharedInstance] cancelRequest:self];
    [self toggleAccessoriesDidStopCallBack];
}

還有一個(gè)比較重要的增加hook的方法,需要自定義個(gè)對(duì)象,實(shí)現(xiàn)YTKRequestAccessory協(xié)議定義的一些方法來(lái)hook一些動(dòng)作

- (void)addAccessory:(id<YTKRequestAccessory>)accessory {
    // 因?yàn)榭赡苡卸鄠€(gè)hook對(duì)象,所以添加到一個(gè)數(shù)組中,調(diào)用的時(shí)候也是遍歷數(shù)組調(diào)用
    if (!self.requestAccessories) {
        self.requestAccessories = [NSMutableArray array];
    }
    [self.requestAccessories addObject:accessory];
}

YTKRequest

這里主要實(shí)現(xiàn)了一些緩存策略,重寫了父類的start方法

- (void)start {
    if (self.ignoreCache) {
        [super start];
        return;
    }

    // 查看緩存時(shí)間是否過(guò)期
    if ([self cacheTimeInSeconds] < 0) {
        [super start];
        return;
    }

    // 查看本地的緩存版本號(hào)和當(dāng)前緩存判斷是否匹配
    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;
    }

    // 查看緩存時(shí)間是否過(guò)期
    int seconds = [self cacheFileDuration:path];
    if (seconds < 0 || seconds > [self cacheTimeInSeconds]) {
        [super start];
        return;
    }

    // 加載緩存數(shù)據(jù)
    _cacheJson = [NSKeyedUnarchiver unarchiveObjectWithFile:path];
    if (_cacheJson == nil) {
        [super start];
        return;
    }

    _dataFromCache = YES;
    // 調(diào)用緩存結(jié)束回調(diào)
    [self requestCompleteFilter];
    YTKRequest *strongSelf = self;
    [strongSelf.delegate requestFinished:strongSelf];
    if (strongSelf.successCompletionBlock) {
        strongSelf.successCompletionBlock(strongSelf);
    }
    [strongSelf clearCompletionBlock];
}

緩存是存放在本地文件中的,文件名用一些關(guān)鍵字的字符串拼接并md5來(lái)表示:

- (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

這個(gè)類主要負(fù)責(zé)一些配置的工作,配置baseUrl,cdnUrl等工作,內(nèi)部沒(méi)有什么具體的實(shí)現(xiàn),在其他類中獲取這個(gè)類的配置信息

YTKBatchRequestAgent、YTKBatchRequest

用于方便地發(fā)送批量的網(wǎng)絡(luò)請(qǐng)求,YTKBatchRequest是一個(gè)容器類,它可以放置多個(gè) YTKRequest 子類,并統(tǒng)一處理這多個(gè)網(wǎng)絡(luò)請(qǐng)求的成功和失敗。

在如下的示例中,我們發(fā)送了4個(gè)批量的請(qǐng)求,并統(tǒng)一處理這4個(gè)請(qǐng)求同時(shí)成功的回調(diào)。


- (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");
    }];
}

內(nèi)部實(shí)現(xiàn),start方法遍歷所有request,并調(diào)用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];
    }
}

在成功回調(diào)中,有一個(gè)計(jì)數(shù)器,判斷所有請(qǐng)求是否都已經(jīng)完成

- (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

用于管理有相互依賴的網(wǎng)絡(luò)請(qǐng)求,它實(shí)際上最終可以用來(lái)管理多個(gè)拓?fù)渑判蚝蟮木W(wǎng)絡(luò)請(qǐng)求。

以下是具體的代碼示例,在示例中,我們?cè)趕endChainRequest方法中設(shè)置好了Api相互的依賴,然后。 我們就可以通過(guò)chainRequestFinished回調(diào)來(lái)處理所有網(wǎng)絡(luò)請(qǐng)求都發(fā)送成功的邏輯了。如果有任何其中一個(gè)網(wǎng)絡(luò)請(qǐng)求失敗了,則會(huì)觸發(fā)chainRequestFailed回調(diào)。

- (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
}

內(nèi)部實(shí)現(xiàn),定義一個(gè)_nextRequestIndex,初始化為0,_requestArray請(qǐng)求數(shù)組,_requestCallbackArray請(qǐng)求回調(diào)數(shù)組

- (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.");
    }
}
// 順序執(zhí)行請(qǐng)求,_nextRequestIndex++
- (BOOL)startNextRequest {
    if (_nextRequestIndex < [_requestArray count]) {
        YTKBaseRequest *request = _requestArray[_nextRequestIndex];
        _nextRequestIndex++;
        request.delegate = self;
        [request start];
        return YES;
    } else {
        return NO;
    }
}
// 請(qǐng)求成功回調(diào)
- (void)requestFinished:(YTKBaseRequest *)request {
    // 獲取當(dāng)前請(qǐng)求的回調(diào),并調(diào)用其回調(diào),回調(diào)中需要用戶自己去再次去add一個(gè)新的request
    NSUInteger currentRequestIndex = _nextRequestIndex - 1;
    ChainCallback callback = _requestCallbackArray[currentRequestIndex];
    callback(self, request);
    // 當(dāng)不能繼續(xù)執(zhí)行請(qǐng)求時(shí),結(jié)束本次chain請(qǐng)求,調(diào)用完成回調(diào)
    if (![self startNextRequest]) {
        [self toggleAccessoriesWillStopCallBack];
        if ([_delegate respondsToSelector:@selector(chainRequestFinished:)]) {
            [_delegate chainRequestFinished:self];
            [[YTKChainRequestAgent sharedInstance] removeChainRequest:self];
        }
        [self toggleAccessoriesDidStopCallBack];
    }
}

YTKNetworkPrivate

定義一些內(nèi)部使用的工具方法

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡(jiǎn)書系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

推薦閱讀更多精彩內(nèi)容