首先

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

最近也是看了YTKNetwork的源码和相关博客,站在前辈的肩膀上写下一些自己关于网络层的解读。

与业务层对接方式

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

  • 集约型:

    最典型就属于上面说的AFNetworking、Alamofire,发起网络请求都集中在一个类上,请求回调通过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]  网络请求

[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  [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:可以理解为一个工具类,拼接地址,提供加密方法,定义分类等。

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

@interface YTKBatchRequest()

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

@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

最后

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

源码解析之–网络层YTKNetwork的更多相关文章

  1. iOS网络请求-AFNetworking源码解析

    趁着端午节日,自己没有什么过多的安排,准备花4-5天左右,针对网络请求源码AFNetworking和YTKNetwork进行解析以及这两年多iOS实际开发经验(其实YTKNetwork也是对AFNet ...

  2. Java生鲜电商平台-SpringCloud微服务架构中网络请求性能优化与源码解析

    Java生鲜电商平台-SpringCloud微服务架构中网络请求性能优化与源码解析 说明:Java生鲜电商平台中,由于服务进行了拆分,很多的业务服务导致了请求的网络延迟与性能消耗,对应的这些问题,我们 ...

  3. [源码解析] 深度学习流水线并行Gpipe(1)---流水线基本实现

    [源码解析] 深度学习流水线并行Gpipe(1)---流水线基本实现 目录 [源码解析] 深度学习流水线并行Gpipe(1)---流水线基本实现 0x00 摘要 0x01 概述 1.1 什么是GPip ...

  4. [源码解析] 深度学习流水线并行 GPipe(3) ----重计算

    [源码解析] 深度学习流水线并行 GPipe(3) ----重计算 目录 [源码解析] 深度学习流水线并行 GPipe(3) ----重计算 0x00 摘要 0x01 概述 1.1 前文回顾 1.2 ...

  5. [源码解析] NVIDIA HugeCTR,GPU版本参数服务器--- (2)

    [源码解析] NVIDIA HugeCTR,GPU版本参数服务器--- (2) 目录 [源码解析] NVIDIA HugeCTR,GPU版本参数服务器--- (2) 0x00 摘要 0x01 总体流程 ...

  6. 【原】Android热更新开源项目Tinker源码解析系列之三:so热更新

    本系列将从以下三个方面对Tinker进行源码解析: Android热更新开源项目Tinker源码解析系列之一:Dex热更新 Android热更新开源项目Tinker源码解析系列之二:资源文件热更新 A ...

  7. 【原】Android热更新开源项目Tinker源码解析系列之一:Dex热更新

    [原]Android热更新开源项目Tinker源码解析系列之一:Dex热更新 Tinker是微信的第一个开源项目,主要用于安卓应用bug的热修复和功能的迭代. Tinker github地址:http ...

  8. 【原】Android热更新开源项目Tinker源码解析系列之二:资源文件热更新

    上一篇文章介绍了Dex文件的热更新流程,本文将会分析Tinker中对资源文件的热更新流程. 同Dex,资源文件的热更新同样包括三个部分:资源补丁生成,资源补丁合成及资源补丁加载. 本系列将从以下三个方 ...

  9. 多线程爬坑之路-Thread和Runable源码解析之基本方法的运用实例

    前面的文章:多线程爬坑之路-学习多线程需要来了解哪些东西?(concurrent并发包的数据结构和线程池,Locks锁,Atomic原子类) 多线程爬坑之路-Thread和Runable源码解析 前面 ...

随机推荐

  1. sql数据库优化技巧汇总

    (转)SQL 优化原则 一.问题的提出 在应用系统开发初期,由于开发数据库数据比较少,对于查询SQL语句,复杂视图的的编写等体会不出SQL语句各种写法的性能优劣,但是如果将应用系统提交实际应用后,随着 ...

  2. Collection View 自定义布局(custom flow layout)

    Collection view自定义布局 一般我们自定义布局都会新建一个类,继承自UICollectionViewFlowLayout,然后重写几个方法: prepareLayout():当准备开始布 ...

  3. Elasticsearch客户端大全 http://www.searchtech.pro/elasticsearch-clients

    Elasticsearch有各种语言的客户端,下面一一列出来: Perl ElasticSearch.pm: Perl客户端 Python pyes: Python客户端pyelasticsearch ...

  4. Hive的MoveTask错误

    最近在部署Hive上线,结果在线上线下同时出现了MoveTask报错的现象,虽然两者错误的日志以及错误信息一样,但是经过分析解决又发现两者的原因是不一样的. 首先线下的错误日志: 2015-05-18 ...

  5. [BZOJ 3282] Tree 【LCT】

    题目链接:BZOJ - 3282 题目分析 这道题是裸的LCT,包含 Link , Cut 和询问两点之间的路径信息. 写代码时出现的错误:Access(x) 的循环中应该切断的是原来的 Son[x] ...

  6. 【POJ3710】Christmas Game (博弈-树上的删边问题)

    [题目] Description Harry and Sally were playing games at Christmas Eve. They drew some Christmas trees ...

  7. jsp分页技术

    1.以下为分页类: import java.io.Serializable;  import java.util.List;    import org.apache.commons.lang.bui ...

  8. Spark:Master High Availability(HA)高可用配置的2种实现

    Spark Standalone集群是Master-Slaves架构的集群模式,和大部分的Master-Slaves结构集群一样,存在着Master单点故障的问题.如何解决这个单点故障的问题,Spar ...

  9. 并行HASH JOIN小表广播问题

    SQL语句: SELECT /*+parallel(t1 16)*/ T1.DATA_DATE, T1.ACCT_NO, T1.ACCT_ORD, T1.ACCT_NO_PK, T1.ACCT_BAL ...

  10. 从 mian 函数开始一步一步分析 nginx 执行流程(一)

    如不做特殊说明,本博客所使用的 nginx 源码版本是 1.0.14,[] 中是代码所在的文件! 我们先贴出 main 函数的部分代码: [core/nginx.c] int ngx_cdecl ma ...