首先

  关于网络层最先可能想到的是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. 编程框架—Autofac

    Autofac是一款轻量级的IOC框架,性能高. Autofac基本使用步骤: 1.创建容器建造者(Builder): 2.对Builder注册类型. 3.Buildder创建容器(Container ...

  2. classpath目录

    WEB-INF/ 是资源目录, 客户端不能直接访问, 这话是没错,不过现在的IDE编译器在编译时会把src下的文件(是文件,不是.java)移到WEB-INF/classes下.不过值得注意的是,sp ...

  3. ARCI--做事情的重要方法论

    很多朋友都可能碰到这样的情况,有一个任务曾经开会讨论过,目标,时间,参与人都有提到,但是最终就是不了了之,没有下文了,而且后面想起来,要追究责任的时候,发现似乎都没有责任,无从追究.如果这种情况出现, ...

  4. GemFire

    一.GemFire是什么?   如果你了解Redis或memCached,那么恭喜,你很快就能理解GemFire是什么,没错,你可以把它理解为一个增强版的Redis,具体在哪些方面增强,我们日后慢慢聊 ...

  5. A simple brute force problem.

    hdu4971:http://acm.hdu.edu.cn/showproblem.php?pid=4971 题意:给你n个项目,每完成一个项目会有一定的收益,但是为了完成某个项目,要先学会一些技能, ...

  6. 14.6.8 Configuring the InnoDB Master Thread IO Rate 配置InnoDB 主线程IO 速率:

    14.6.8 Configuring the InnoDB Master Thread IO Rate 配置InnoDB 主线程IO 速率: 主线程 在InnoDB 是一个线程 执行各种任务在后台. ...

  7. BLE开发的各种坑

    这段时间在做低功耗蓝牙(BLE)应用的开发(并不涉及蓝牙协议栈).总体感觉 Android BLE 还是不太稳定,开发起来也是各种痛苦.这里记录一些杂项和开发中遇到的问题及其解决方法,避免大家踩坑.本 ...

  8. 游戏开发设计模式之命令模式(unity3d 示例实现)

    博主才学尚浅,难免会有错误,尤其是设计模式这种极富禅意且需要大量经验的东西,如果哪里书写错误或有遗漏,还请各位前辈指正. 打 算写设计模式的目的就是,首先自己可以理清思路,还有就是国内的设计模式资料很 ...

  9. 【转】VSync Count 垂直同步

    原文:http://blog.csdn.net/yesy10/article/details/7794556 Unity3D中新建一个场景空的时候,帧速率(FPS总是很低),大概在60~70之间.一直 ...

  10. hdu 4465 Candy(二次项概率)

    题目链接:http://acm.hdu.edu.cn/showproblem.php?pid=4465 参考博客:http://www.cnblogs.com/goagain/archive/2012 ...