该框架是一个通用的网络层,能够供给不一样app的业务层调用。该框架封装了AFNetworking,并且有些地方借鉴了YTKNetwork的设计思路:以对象的形式封装并管理请求。html
它在功能上支持:ios
GitHub连接:SJNetworkgit
项目里面附有demo:SJNetworkDemo项目文件夹程序员
在看架构图以前,先简单介绍一下该框架里每一个类的职责:github
类名 | 职责 |
---|---|
SJNetwork | 总头文件,只须要引入该文件便可使用该框架全部功能 |
SJNetworkProtocol | 定制了请求结束后的处理方法,从此可能还会扩展 |
SJNetworkHeader | 定义了回调block和一些枚举类型 |
SJNetworkManager | 与业务层直接对接的类,包含了除配置接口外全部关于网络请求功能的接口 |
SJNetworkBaseEngine | 全部负责发送请求类的基类 |
SJNetworkRequestEngine | 发送(GET,POST,PUT,DELETE)请求的类:支持设置缓存有效期,读,写和清理缓存 |
SJNetworkUploadEngine | 发送上传请求的类:支持设置图片类型和压缩上传,批量上传 |
SJNetworkDownloadEngine | 发送下载请求的类:支持断点续传和后台下载 |
SJNetworkRequestModel | 请求对象类:持有某个网络请求的一些数据;好比请求url,请求体等) |
SJNetworkCacheManager | 缓存处理类:缓存的写入,读取,删除 |
SJNetworkConfig | 配置类:配置服务器地址,debug模式等 |
SJNetworkUtils | 工具类:能够用于生成缓存路径,app版本号等 |
SJNetworkRequestPool | 请求对象池:用于存放正在进行的请求对象 |
SJNetworkCacheInfo | 缓存元数据:记录其对应缓存数据的信息(版本号,缓存过时时间) |
SJNetworkDownloadResumeDataInfo | 未下载完成数据的元数据:记录未下载完成数据的信息(已经下载的比例,下载数据的总长度,已经下载数据的长度) |
从架构图中能够看出:objective-c
SJNetworkManager
的接口来发送请求(或进行操做请求等操做),而实际进行工做的类实际上是SJNetworkRequestEngine
,SJNetworkUploadEngine
,SJNetworkDownloadEngine
,SJNetworkCacheManager
这些类。SJNetworkRequestModel
实例来管理,其管理者为SJNetworkRequestPool
。SJNetworkConfig
和SJNetworkUtils
是能够在任意地方调用的,由于要常常获取用户所作的一些配置以及调用一些经常使用的工具类方法。Step1:下载与导入框架编程
经过Cocoa pods:api
pod 'SJNetwork'
数组
最新的版本号为1.2.0缓存
或者 手动将 SJNetwork
文件夹拖入到工程里面。
Step2:引入头文件:
若是是使用了Cocoapods:
import <SJNetwork/SJNetwork.h>
复制代码
若是是手动拖入:
#import "SJNetwork.h"
复制代码
由于配置对象是一个单例(SJNetworkConfig
),因此能够在项目任何地方来使用。看一下该框架支持哪些配置项:
[SJNetworkConfig sharedConfig].baseUrl = @"http://v.juhe.cn";
复制代码
[SJNetworkConfig sharedConfig].defailtParameters = @{@"app_version":[SJNetworkUtils appVersionStr],
@"platform":@"iOS"};
复制代码
默认参数会拼接在全部请求的请求体中;
若是是GET请求,则拼接在url里面。
[SJNetworkConfig sharedConfig].timeoutSeconds = 30;
复制代码
超时时间默认为20s。
[SJNetworkConfig sharedConfig].debugMode = YES;//默认为NO
复制代码
若是设置debug模式为YES,则会打印出不少详细的log,便于调试。
若是设置为NO,则没有log。
[[SJNetworkConfig sharedConfig] addCustomHeader:@{@"token":@"2j4jd9s74bfm9sn3"}];
复制代码
或者
[[SJNetworkManager sharedManager] addCustomHeader:@{@"token":@"2j4jd9s74bfm9sn3"}];//实际上调用了SJNetworkConfig的addCustomHeader方法
复制代码
添加的请求头键值对会自动添加到全部的请求头中;
若是键值对原来不存在,则添加;若是原来存在,则替换原有的。
在这里定义GET,POST,PUT,DELETE请求为普通的网络请求,是由SJNetworkRequestManager
实现的。全部的这些普通的网络请求都支持写入和读取缓存,可是默认是不支持的,由用户来决定是否写入,读取缓存。
发送一个不支持写入和读取缓存的POST请求:
[[SJNetworkManager sharedManager] sendPostRequest:@"toutiao/index"
parameters:@{@"type":@"top",
@"key" :@"0c60"}
success:^(id responseObject) {
NSLog(@"request succeed:%@",responseObject);
} failure:^(NSURLSessionTask *task, NSError *error, NSInteger statusCode) {
NSLog(@"request failed:%@",error);
}];
复制代码
发送一个支持写入有效时间为180秒并在缓存有效时读取缓存的POST请求:
[[SJNetworkManager sharedManager] sendPostRequest:@"toutiao/index"
parameters:@{@"type":@"top",
@"key" :@"0c60"}
loadCache:YES
cacheDuration:180
success:^(id responseObject) {
NSLog(@"request succeed:%@",responseObject);
} failure:^(NSURLSessionTask *task, NSError *error, NSInteger statusCode) {
NSLog(@"request failed:%@",error);
}];
复制代码
cacheDuration:缓存有效的时间,单位为秒。
- 若是大于0,则进行缓存。
- 若是小于等于0,则不进行缓存。
loadCache:若是设置为YES,则在发起请求前,先查看是否缓存有效(若是设置为NO,则不管有没有缓存,都进行网络请求):
- 若是缓存存在并有效,则返回缓存,不进行网络请求;
- 若是缓存不存在,或者存在但失效(时间过时)则删除缓存(若是缓存存在)并进行网络请求。
完整的带有缓存判断的普通网络请求的流程图:
缓存管理是由SJNetworkCacheManager
的单例来实现的,功能分为缓存的读取,删除和计算。先来看一下缓存的读取:
该框架支持单个缓存的读取和多个缓存的读取:
若是知道这个缓存对应的请求url,method,请求体,就能尝试获取它所对应的缓存对象:
举个例子,若是想获取上面有写入缓存的网络请求的缓存,就能够用以下API:
[[SJNetworkManager sharedManager] loadCacheWithUrl:@"toutiao/index"
method:@"POST"
parameters:@{@"type":@"top",
@"key" :@"0c60"}
completionBlock:^(id _Nullable cacheObject) {
NSLog(@"%@",cacheObject);
}];
复制代码
注意,在缓存的读取过程当中会有如下几种状况:
若是这个请求对应的缓存不存在,则会从block里传过来nil。
若是这个请求对应的缓存存在,可是失效了(有效期过了),则这个缓存就会被清除掉,并会在block里传过来nil。
若是这个请求对应的缓存存在并有效,则会从block里传过来缓存对象。
若是有些请求使用的是同一个url(可是不一样的请求方法或者参数)并作了缓存,那么经过以下方法能够获取它们的缓存:
[[SJNetworkManager sharedManager] loadCacheWithUrl:@"toutiao/index"
completionBlock:^(NSArray * _Nullable cacheArr) {
NSLog(@"%@",cacheArr);
}];
复制代码
若是有些请求使用的是同一个url以及请求方法,可是请求参数不一样,那么经过以下方法能够获取它们的缓存(用数组保存):
[[SJNetworkManager sharedManager] loadCacheWithUrl:@"toutiao/index"
method:@"POST"
completionBlock:^(NSArray * _Nullable cacheArr) {
NSLog(@"%@",cacheArr);
}];
复制代码
如今咱们知道这个框架在缓存的读取上支持单个与批量读取,接下来看一下缓存的删除:
一样地,该框架也支持缓存的单个与批量删除。
若是你想删除属于某个特定url,method,请求参数的请求的缓存,可使用下面这个API:
[[SJNetworkManager sharedManager] clearCacheWithUrl:@"toutiao/index"
method:@"POST"
parameters:@{@"type":@"top",
@"key" :@"0c60"}
completionBlock:^(BOOL isSuccess) {
if (isSuccess) {
NSLog(@"Clearing cache successfully!");
}
}];
复制代码
若是你想删除使用的是同一个url(可是不一样的请求方法或者参数)的请求的缓存,可使用下面这个API:
[[SJNetworkManager sharedManager] clearCacheWithUrl:@"toutiao/index"
completionBlock:^(BOOL isSuccess) {
if (isSuccess) {
NSLog(@"Clearing cache successfully!");
}
}];
复制代码
若是你想删除使用同一个url和method,可是不一样请求参数的的请求的缓存,可使用下面这个API:
[[SJNetworkManager sharedManager] clearCacheWithUrl:@"toutiao/index"
method:@"POST"
withCompletionBlock:^(BOOL isSuccess) {
if (isSuccess) {
NSLog(@"Clearing cache successfully!");
}
}];
复制代码
看完了缓存的读取和删除,咱们来看一下缓存的计算:
缓存的计算只提供了一个接口,在block回调的时候会回传一个文件个数,全部缓存的大小,以及带有KB或MB的字符串:
[[SJNetworkManager sharedManager] calculateCacheSizeWithCompletionBlock:^(NSUInteger fileCount, NSUInteger totalSize, NSString *totalSizeString) {
NSLog(@"file count :%lu and total size:%lu total size string:%@",(unsigned long)fileCount,(unsigned long)totalSize, totalSizeString);
}];
复制代码
fileCount:缓存文件的个数,为整数
total size:单位为字节
totalSizeString:带有KB和MB转化的字符串:在1024*1024字节之内以KB为单位;之外以MB为单位。例如:
file count :5 and total size:1298609 total size string:1.2385 MB
**注意:**所计算的缓存包括全部普通请求的缓存以及未下载完成,之后须要继续下载的数据。
上传图片的功能是由SJNetworkUploadManager`类的单例实现的:支持上传单个与多个
UIImage``对象,能够设置压缩比率(不设置时默认为1,不压缩)。
上传单个UIImage对象,上传前不对图片进行压缩:
[[SJNetworkManager sharedManager] sendUploadImageRequest:@"api"
parameters:nil
image:image_1
name:@"color"
mimeType:@"png"
progress:^(NSProgress *uploadProgress)
{
self.progressView.observedProgress = uploadProgress;
} success:^(id responseObject) {
NSLog(@"upload succeed");
} failure:^(NSURLSessionTask *task, NSError *error, NSInteger statusCode, NSArray<UIImage *> *uploadFailedImages) {
NSLog(@"upload failed, failed images:%@",uploadFailedImages);
}];
复制代码
上传多个UIImage对象,压缩比率为0.5:
[[SJNetworkManager sharedManager] sendUploadImagesRequest:@"api"
parameters:nil
images:@[image_1,image_2]
compressRatio:0.5
name:@"images"
mimeType:@"jpg"
progress:^(NSProgress *uploadProgress)
{
self.progressView.observedProgress = uploadProgress;
} success:^(id responseObject) {
NSLog(@"upload succeed");
} failure:^(NSURLSessionTask *task, NSError *error, NSInteger statusCode, NSArray<UIImage *> *uploadFailedImages) {
NSLog(@"upload failed, failed images:%@",uploadFailedImages);
}];
复制代码
这里的mimeType能够设置为jpg/JPG, png/PNG, jpeg/JPEG,做为图片上传到服务器时的类型。须要注意的是,若是mimeType为png/PNG的时候,设置的压缩比率就是无效的,将会必定以原图大小上传。
考虑到上传图片的服务器可能与普通请求的服务器不一样,特地增长了一个参数:ignoreBaseUrl
。若是该布尔值设置为YES,则在SJNetworkConfig
单例里面设置的baseUrl
就会被忽略掉,用户须要在请求的第一个参数里面将完整的请求url写进去:
[[SJNetworkManager sharedManager] sendUploadImagesRequest:@"http://uploads.im/api"
ignoreBaseUrl:YES
parameters:nil
images:@[image_1,image_2]
compressRatio:0.5
name:@"images"
mimeType:@"jpg"
progress:^(NSProgress *uploadProgress)
{
self.progressView.observedProgress = uploadProgress;
} success:^(id responseObject) {
NSLog(@"upload succeed");
} failure:^(NSURLSessionTask *task, NSError *error, NSInteger statusCode, NSArray<UIImage *> *uploadFailedImages) {
NSLog(@"upload failed, failed images:%@",uploadFailedImages);
}];
复制代码
还有一个方法,就是强制更改SJNetworkConfig
单例设置的baseUrl
:
[SJNetworkConfig sharedConfig].baseUrl = @"http://uploads.im";
[[SJNetworkManager sharedManager] sendUploadImagesRequest:@"api"
ignoreBaseUrl:NO
parameters:nil
images:@[image_3,image_4]
compressRatio:0.5
name:@"color"
mimeType:@"png"
progress:^(NSProgress *uploadProgress)
{
self.progressView.observedProgress = uploadProgress;
} success:^(id responseObject) {
NSLog(@"upload succeed");
} failure:^(NSURLSessionTask *task, NSError *error, NSInteger statusCode, NSArray<UIImage *> *uploadFailedImages) {
NSLog(@"upload failed, failed images:%@",uploadFailedImages);
}];
复制代码
虽然看上去不是很优雅,但却也是可行的:在请求全部普通网络请求以前将baseUrl再次改回去便可。
暂时该框架还没法支持多个baseUrl的功能,之后若是有研究到的话就会添加上去。
下载功能是由SJNetworkDownloadManager
的单例来实现的,支持断点续传以及后台下载。
NSURLSessionDownloadTask
。在手机退出前台,进入后台后后仍然能够下载。NSURLSessionDataTask
。在手机退出前台后没法继续下载,可是经过框架内部的自动恢复下载机制,在回到前台后就会继续以前的下载。并且结合了NSOutputStream
实例,将下载下来的数据一点一点的写在沙盒里面,减小了内存的压力,也就是支持大文件下载。综上会是由四种状况:
支持断点续传 | 不支持断点续传 | |
---|---|---|
支持后台下载 | ✅ | ✅ |
不支持后台下载 | ✅ | ✅ |
默认配置为:支持断点续传,不支持后台下载(由于除非是音乐视频类等特殊app,后台下载的操做可能会被Apple拒掉)。
[[SJNetworkManager sharedManager] sendDownloadRequest:@"wallpaper.jpg"
downloadFilePath:_imageFileLocalPath
progress:^(NSInteger receivedSize, NSInteger expectedSize, CGFloat progress)
{
self.progressView.progress = progress;
} success:^(id responseObject) {
NSLog(@"Download succeed!");
} failure:^(NSURLSessionTask *task, NSError *error, NSString *resumableDataPath) {
NSLog(@"Download failed!");
}];
复制代码
若是支持断点续传,在返回失败的回调里面会传过来未下载完成数据的路径:
resumableDataPath
。
[[SJNetworkManager sharedManager] sendDownloadRequest:@"half-eatch.jpg"
downloadFilePath:_imageFileLocalPath
resumable:NO
backgroundSupport:NO
progress:^(NSInteger receivedSize, NSInteger expectedSize, CGFloat progress)
{
self.progressView.progress = progress;
} success:^(id responseObject) {
NSLog(@"Download succeed!");
} failure:^(NSURLSessionTask *task, NSError *error, NSString *resumableDataPath) {
NSLog(@"Download failed!");
}];
复制代码
[[SJNetworkManager sharedManager] sendDownloadRequest:@"universe.jpg"
downloadFilePath:_imageFileLocalPath
resumable:YES
backgroundSupport:YES
progress:^(NSInteger receivedSize, NSInteger expectedSize, CGFloat progress)
{
self.progressView.progress = progress;
} success:^(id responseObject) {
NSLog(@"Download succeed!");
} failure:^(NSURLSessionTask *task, NSError *error, NSString *resumableDataPath) {
NSLog(@"Download failed!");
}];
复制代码
[[SJNetworkManager sharedManager] sendDownloadRequest:@"iceberg.jpg"
downloadFilePath:_imageFileLocalPath
resumable:NO
backgroundSupport:YES
progress:^(NSInteger receivedSize, NSInteger expectedSize, CGFloat progress)
{
self.progressView.progress = progress;
} success:^(id responseObject) {
NSLog(@"Download succeed!");
} failure:^(NSURLSessionTask *task, NSError *error, NSString *resumableDataPath) {
NSLog(@"Download failed!");
}];
复制代码
和上传同样,下载接口也都支持是否忽略baseUrl:
[[SJNetworkManager sharedManager] sendDownloadRequest:@"http://oih3a9o4n.bkt.clouddn.com/wallpaper.jpg"
ignoreBaseUrl:YES
downloadFilePath:_imageFileLocalPath
progress:^(NSInteger receivedSize, NSInteger expectedSize, CGFloat progress)
{
self.progressView.progress = progress;
} success:^(id responseObject) {
NSLog(@"Download succeed!");
} failure:^(NSURLSessionTask *task, NSError *error, NSString *resumableDataPath) {
NSLog(@"Download failed!");
}];
复制代码
全部的下载请求都支持暂停,恢复和取消操做。而且这些操做都支持单独与批量操做:
暂停单独的下载请求:
[[SJNetworkManager sharedManager] suspendDownloadRequest:@"universe.jpg"];
复制代码
暂停多个下载请求:
[[SJNetworkManager sharedManager] suspendDownloadRequests:@[@"universe.jpg",@"wallpaper.jpg"]];
复制代码
暂停全部下载请求:
[[SJNetworkManager sharedManager] suspendAllDownloadRequests];
复制代码
恢复单独的正在暂停的下载请求:
[[SJNetworkManager sharedManager] resumeDownloadReqeust:@"universe.jpg"];
复制代码
恢复多个正在暂停的下载请求:
[[SJNetworkManager sharedManager] resumeDownloadReqeusts:@[@"universe.jpg",@"wallpaper.jpg"]];
复制代码
恢复全部正在暂停的下载请求:
[[SJNetworkManager sharedManager] resumeAllDownloadRequests];
复制代码
取消单独的下载请求:
[[SJNetworkManager sharedManager] cancelDownloadRequest:@"universe.jpg"];
复制代码
取消多个下载请求:
[[SJNetworkManager sharedManager] cancelDownloadRequests:@[@"universe.jpg",@"wallpaper.jpg"]];
复制代码
取消全部下载请求:
[[SJNetworkManager sharedManager] cancelAllDownloadRequests];
复制代码
在该框架中,不管是普通的下载请求,上传请求和下载请求,在发送请求以前都将用户传入的参数保存在专门的请求对象SJNetworkRequestModel
的实例里面。而这些实例的管理工做交给了SJNetworkRequestPool
类的单例:
除了添加和移除请求对象之外,SJNetworkRequestPool
对请求的管理还包括:
BOOL remaining = [[SJNetworkManager sharedManager] remainingCurrentRequests];
if (remaining) {
NSLog(@"There is remaining request");
}
复制代码
NSUInteger count = [[SJNetworkManager sharedManager] currentRequestCount];
if (count > 0) {
NSLog(@"There is %lu requests",(unsigned long)count);
}
复制代码
[[SJNetworkManager sharedManager] logAllCurrentRequests];
复制代码
请求的取消也分为单个和批量的取消:
[[SJNetworkManager sharedManager] cancelCurrentRequestWithUrl:@"toutiao/index"
method:@"POST"
parameters:@{@"type":@"top",
@"key" :@"0c60"}];
复制代码
[[SJNetworkManager sharedManager] cancelCurrentRequestWithUrl:@"toutiao/index"];
复制代码
[[SJNetworkManager sharedManager] cancelDownloadRequests:@[@"toutiao/index",@"weixin/query"]];
复制代码
[[SJNetworkManager sharedManager] cancelAllCurrentRequests];
复制代码
若是将debug模式设置为YES,则会打印出不少便于调试的log:
[SJNetworkConfig sharedConfig].debugMode = YES;
复制代码
因为重写了SJNetworkRequestModel
的description
方法,因此在打印该对象的时候,普通的网络请求,上传请求,下载请求都有属于本身的log,在这里举一个普通请求的log:
{
<SJNetworkRequestModel: 0x6040001fc100>
type: ordinary request
method: GET
url: http://v.juhe.cn/toutiao/index
parameters: {
"app_version" = "1.0";
key = 0c60;
platform = iOS;
type = top;
}
loadCache: YES
cacheDuration: 5 seconds
requestIdentifer:b4b36793efabad54a14389cf09bc8133_a6a72ddee1dd86825cb5707c500784f5_7b65261ff298c6a386c89a632bd17b39_30c9b994c268547f38a2f9af6f8c171f
task: <__NSCFLocalDataTask: 0x7f8e075320a0>{ taskIdentifier: 1 } { completed }
}
复制代码
举一个须要获取缓存的网络请求可是遇到缓存过时的状况:
=========== Load cache info failed, reason:Cache is expired, begin to clear cache...
=========== Load cache failed: Cache info is invalid
=========== Faild to load cache, start to sending network request...
=========== Start requesting...
=========== url:http://v.juhe.cn/toutiao/index
=========== method:GET
=========== parameters:{
"app_version" = "1.0";
key = 0c60;
platform = iOS;
type = top;
}
=========== Request succeed!
=========== Request url:http://v.juhe.cn/toutiao/index
=========== Response object:{
code = 200,
msg = "",
data = {}
}
=========== Write cache succeed!
=========== cache object: {
code = 200,
msg = "",
data = {}
}
=========== Cache path: /Users/****/***/***/*******.cacheData
=========== Available duration: 180 seconds
复制代码
在调试这个框架的时候使用了不少网络资源,也看了好多文章,得到了不少帮助,因此不得不提一下:
如今社区里二次封装AFNetworking,上传图片,以及下载器的框架有不少,可是由于老是想写一个属于本身代码风格的框架,并且网络层对我本身仍是有些挑战的,因此想试一试。
除去中间间隔的时间,整个框架基本成型的时间用了有一个多月,可是写全英文的注释,最后的重构(修改了架构,分离了一些类),命名的规范,修改bug,优化等事情又花去了半个月。特别是因为本身对下载这一块不熟,尤为是断点续传,后台下载这两方面更是没有实战经验,在写的时候也花了很多时间。
但愿能多给出宝贵意见和建议,我本身发现有不足的地方也会更新~
本篇已同步到我的博客:传送门
---------------------------- 2018年7月17日更新 ----------------------------
注意注意!!!
笔者在近期开通了我的公众号,主要分享编程,读书笔记,思考类的文章。
由于公众号天天发布的消息数有限制,因此到目前为止尚未将全部过去的精选文章都发布在公众号上,后续会逐步发布的。
并且由于各大博客平台的各类限制,后面还会在公众号上发布一些短小精干,以小见大的干货文章哦~
扫下方的公众号二维码并点击关注,期待与您的共同成长~