美文网首页技术iOS网络JC专题
源码解析之--YTKNetwork网络层

源码解析之--YTKNetwork网络层

作者: Shelin | 来源:发表于2016-05-16 09:51 被阅读4240次
IMG_0974.JPG
首先

关于网络层最先可能想到的是AFNetworking,或者Swift中的Alamofire,直接使用起来也特别的简单,但是稍复杂的项目如果直接使用就显得不够用了,首先第三方耦合不说,就光散落在各处的请求回调就难以后期维护,所以一般会有针对性的再次封装,往往初期可能业务相对简单,考虑的方面较少,后期业务增加可能需要对网络层进行重构,一个好的架构也一定是和业务层面紧密相连的,随业务的增长不断健壮的。
  最近也是看了YTKNetwork的源码和相关博客,站在前辈的肩膀上写下一些自己关于网络层的解读。

与业务层对接方式

常见的与业务层对接方式两种:

  • 集约型:
      最典型就属于上面说的AFNetworkingAlamofire,发起网络请求都集中在一个类上,请求回调通过Block、闭包实现的,Block、闭包回调有比较好的灵活性,可以方便的在任何位置发起请求,同时也可能是不好的地方,网络请求回调散落在各处,不便于维护。
      下面是一个集约型的网络请求,大家使用集约型网络请求有没有遇到这么一个场景,请求回调后需要做比较多的处理,代码量多的时候,会再定义一个私有方法把代码写在里面,在Block中调用在私有方法。其实这样处理本质上和通过代理回调本质上是一样的。
[_manager GET:url parameters:param success:^(AFHTTPRequestOperation *operation, id responseObject) {
     //The data processing, Rendering interface
 } failure:^(AFHTTPRequestOperation *operation, NSError *error) {
                   
 }];

  • 离散型:
      对应的是接下来的YTKNetwork,离散型最大的特点就是一个网络请求对应一个单独的类,在这个类内部封装请求地址、方式、参数、校验和处理请求回来的数据,通常代理回调,需要跨层数据传递时也使用通知回调,比较集中,因为数据处理都放在内部处理了,返回数据的形式(模型化后的数据还是其他)不需要控制器关心,控制器只需要在代理返回的数据可以直接对渲染UI,让Controller更加轻量化。

发起请求

    NSString *userId = @"1";
    GetUserInfoApi *api = [[GetUserInfoApi alloc] initWithUserId:userId];
    [api start];
    api.delegate = self;

Delegate回调

- (void)requestFinished:(YTKBaseRequest *)request {
    NSLog(@"----- succeed ---- %@", request.responseJSONObject);
    //Rendering interface
}
- (void)requestFailed:(YTKBaseRequest *)request {
    NSLog(@"failed");
}

YTKNetwork解析

首先看下YTKNetwork的类文件:


YTKNetwork.png

图解它们之间的调用关系,注意还是理顺关系,看懂这个图应该对源码的理解没有太多问题:

Scrren.png
  • YTKBaseRequest:YTKRequest的父类,定义了Request的相关属性,Block和Delegate。给对外接口默认的实现,以及公共逻辑。
  • YTKRequest:主要对缓存做处理,更新缓存、读取缓存、手动写入缓存,是否忽略缓存。这里采用归档形式缓存,请求方式、根路径、请求地址、请求参数、app版本号、敏感数据拼接再MD5作为缓存的文件名,保证唯一性。还提供设置缓存的保存时长,主要实现是通过获取缓存文件上次修改的时刻距离现在的时间和设置的缓存时长作比较,来判断是否真正发起请求,下面是发起请求的一些逻辑判断:
- (void)start {
    if (self.ignoreCache) {
        //如果忽略缓存 -> 网络请求
        [super start];
        return;
    }

    // check cache time
    if ([self cacheTimeInSeconds] < 0) {
        //验证缓存有效时间 -> 网络请求
        [super start];
        return;
    }

    // check cache version
    long long cacheVersionFileContent = [self cacheVersionFileContent];
    if (cacheVersionFileContent != [self cacheVersion]) {
        //验证缓存版本号,如果不一致 -> 网络请求
        [super start];
        return;
    }

    // check cache existance
    NSString *path = [self cacheFilePath];  //
    NSFileManager *fileManager = [NSFileManager defaultManager];
    if (![fileManager fileExistsAtPath:path isDirectory:nil]) {
        //根据文件路径,验证缓存是否存在,不存在 -> 网络请求
        [super start];
        return;
    }

    // check cache time 上次缓存文件时刻距离现在的时长 与 缓存有效时间 对比
    int seconds = [self cacheFileDuration:path];
    if (seconds < 0 || seconds > [self cacheTimeInSeconds]) {
        //上次缓存文件时刻距离现在的时长 > 缓存有效时间
        [super start];
        return;
    }

    // load cache
    _cacheJson = [NSKeyedUnarchiver unarchiveObjectWithFile:path];
    if (_cacheJson == nil) {    //取出缓存,如果没有 -> 网络请求
        [super start];
        return;
    }

    _dataFromCache = YES;
    //缓存请求成功后的数据
    
    [self requestCompleteFilter];   //代理
    
    YTKRequest *strongSelf = self;
    [strongSelf.delegate requestFinished:strongSelf];
    
    if (strongSelf.successCompletionBlock) {    //block回调
        strongSelf.successCompletionBlock(strongSelf);
    }
    [strongSelf clearCompletionBlock];
}

通过归档存储网络请求的数据:

- (void)saveJsonResponseToCacheFile:(id)jsonResponse {
    if ([self cacheTimeInSeconds] > 0 && ![self isDataFromCache]) {
        NSDictionary *json = jsonResponse;
        if (json != nil) {
            [NSKeyedArchiver archiveRootObject:json toFile:[self cacheFilePath]];
            [NSKeyedArchiver archiveRootObject:@([self cacheVersion]) toFile:[self cacheVersionFilePath]];
        }
    }
}

  • YTKNetworkAgent:真正发起网络请求的类,在addRequest方法里调用AFN的方法,这块可以方便的更换第三方库,还包括一些请求取消,插件的代理方法调用等,所有网络请求失败或者成功都会调用下面这个方法:
- (void)handleRequestResult:(AFHTTPRequestOperation *)operation {
    NSString *key = [self requestHashKey:operation];
    YTKBaseRequest *request = _requestsRecord[key];
    YTKLog(@"Finished Request: %@", NSStringFromClass([request class]));
    if (request) {
        
        BOOL succeed = [self checkResult:request];
        if (succeed) {  //请求成功
            [request toggleAccessoriesWillStopCallBack];    //调用执行加载动画插件
            [request requestCompleteFilter];
            if (request.delegate != nil) {  //请求成功代理回调
                [request.delegate requestFinished:request];
            }
            if (request.successCompletionBlock) {   //请求成功Block回调
                request.successCompletionBlock(request);
            }
            [request toggleAccessoriesDidStopCallBack];
        } else {            //请求失败
            YTKLog(@"Request %@ failed, status code = %ld",
                     NSStringFromClass([request class]), (long)request.responseStatusCode);
            [request toggleAccessoriesWillStopCallBack];    //调用执行加载动画插件
            [request requestFailedFilter];
            if (request.delegate != nil) {      //请求失败代理回调
                [request.delegate requestFailed:request];
            }
            if (request.failureCompletionBlock) {   //请求失败Block回调
                request.failureCompletionBlock(request);
            }
            [request toggleAccessoriesDidStopCallBack];
        }
    }
    [self removeOperation:operation];
    [request clearCompletionBlock];
}
  • YTKNetworkConfig:配置请求根路径、DNS地址。

  • YTKNetworkPrivate:可以理解为一个工具类,拼接地址,提供加密方法,定义分类等。

  • YTKBatchRequestYTKChainRequest:这是YKTNetwork的两个高级用法,批量网络请求和链式的网络请求,相当于一个存放Request的容器,先定义下面属性,finishedCount来记录批量请求的完成的个数:

@interface YTKBatchRequest() <YTKRequestDelegate>
@property (nonatomic) NSInteger finishedCount;
@end

每完成一个请求finishedCount++,直到finishedCount等于所有请求的个数时才回调成功。

#pragma mark - Network Request Delegate

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

给Request绑定一个Index

@interface YTKChainRequest()<YTKRequestDelegate>
@property (assign, nonatomic) NSUInteger nextRequestIndex;
@end

从requestArray数组中依次取出发起网络请求,同时nextRequestIndex++,只要一个请求失败则触发失败的回调:

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

//下一个网络请求
- (BOOL)startNextRequest {
    if (_nextRequestIndex < [_requestArray count]) {
        YTKBaseRequest *request = _requestArray[_nextRequestIndex];
        _nextRequestIndex++;
        request.delegate = self;
        [request start];
        return YES;
    } else {
        return NO;
    }
}
最后

最近也是看得比写代码多,大都一些源码和博客,种类也比较泛,读懂作者的思路还是收获颇多,往往有时候会要上好几遍,每一遍都有些新的收获,贵在坚持了https://github.com/ShelinShelin/SourceCodeAnalyze

相关文章

  • YTKNetwork

    YTKNetwork集成教程以及相关问题思考 源码解析之--YTKNetwork网络层 YTKNetwork源码解...

  • 源码解析之--YTKNetwork网络层

    首先 关于网络层最先可能想到的是AFNetworking,或者Swift中的Alamofire,直接使用起来也特别...

  • 源码解析之--YTKNetwork网络层

    转载链接:http://www.jianshu.com/p/521a6437a0b6参考上面链接才稍微看懂源码的,...

  • ios三方库解析

    YYCache 源码解析 YTKNetwork 源码解析 MJRefresh 源码解析 VVeboTableVie...

  • iOS 一些框架源码解析

    YYCache 源码解析 YTKNetwork源码解析 MJRefresh 源码解析 VVeboTableView...

  • YTKNetWork源码解析

    背景: YTKNetWork是一个开源的第三方网络请求框架,具有比较好的网络请求缓存机制的控制。近期项目中想要采取...

  • YTKNetwork源码解析

    对于iOS开发者来说,就算是没有用过YTKNetwork框架,应该也见过,听过了。它是猿题库技术团队开源的一个网络...

  • YTKNetwork源码解析

    对于大多数app来说,业务量到一定级别,为了可维护性和框架的合理性,通常都需要设计专门的网络层,将业务逻辑中的所有...

  • YTKNetwork源码解析

    YTKNetwork 是猿题库 iOS 研发团队基于 AFNetworking 封装的 iOS 网络库,其实现了一...

  • 小尝YTKNetwork

    一、什么是YTKNetwork YTKNetwork是一个基于AFNetworking的网络层封装。 二、包括那几...

网友评论

  • ea44eccdf584:使用YTKNetwork出现 _block_invoke_2是什么情况?
  • Mao_Chen:返回数据错误,报 NSLocalizedDescription=Request failed: unacceptable content-type: text/html} 更改了responseSerializer.acceptableContentTypes为text/html也没用
  • GJCode:写的不错,支持下,赞赞赞
  • 酒红色T恤:一直在看你的《iOS数据库离线缓存思路和网络层封装》,有的地方不太懂,大概思路能懂
  • DirkChen:拜读一下

本文标题:源码解析之--YTKNetwork网络层

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