最近工作不是很忙,表示最近也在一直的学习理论知识,但是光是单纯的看书和看面试题,感觉有点大学期末考试的赶脚,所以还是想着要把理论和实践多结合,所以陆续把工作中的用到的东西整理成一个个Demo 这样,和尽量的封装一下子,这样也算是平时的一点积累。
首先是觉得可以把网络的小模块搞一搞,虽然AFN在ios界是神作,地位不可动摇,但是我们这还是不能单单只会AFN,必备的网络知识还是要了解,题外话图解HTTP这本书还是很不错的,生动形象,简单易懂的讲解了网络知识,强烈像我一样半路出家的人看一看,百度云链接。大神请绕行
废话说了一筐还是直接说代码吧,先看一下目录结构
其实东西很简单,主要就是实现一个多线程下载的功能,在DownloadManagment 类中有一个字典,每次调用下载的时候都会建立一个NSURLSessionDownloadTask 下载任务,然后以URL为Key 把他存到字典中,这样这样可以防止重复执行下载任务,而且下载过程中可以暂停任务也就是支持断点续传,而且可以把执行多个任务,还有在任务之间进行切换。不过之前源代码实在不敢吐槽,居然把下载的一些缓存啥的都存到了数据库里,感觉好突兀,所以自己改成了存成归档文件,怎么存都可以随你喜欢啦,只不过不喜欢那么搞而已。大概把功能说道这里,下面就是最激动人心的贴代码时间。
首先是DownloadManagment类,负责对外管理类这么搞其实并说不清具体用到了什么设计模式,只不过各个牛逼的框架都会这么用。
@interface DownloadManagment ()
@property(nonatomic, strong)NSMutableDictionary *downloadDic;//把不同的任务都存到了这个字典里方便管理
@end
@implementation DownloadManagment
+ (instancetype)shareDownloadManagment
{
static DownloadManagment *downloadManagment = nil;
static dispatch_once_t onceToken;
dispatch_once(&onceToken, ^{
downloadManagment = [[DownloadManagment alloc]init];
});
return downloadManagment;
}
// 初始化方法
- (instancetype)init
{
self = [super init];
if (self) {
self.downloadDic = [NSMutableDictionary dictionary];
}
return self;
}
// 根据URL添加一个下载类
- (Download *)addDownloadWithUrl:(NSString *)url
{
// 先从字典里面取到对应的下载
Download *download = self.downloadDic[url];
if (download == nil) {// 如果字典里面没有 我们就创建一个
download = [[Download alloc]initWithURL:url];
//添加到我们的字典当中
[self.downloadDic setObject:download forKey:url];
}
// 下载完成以后 让单例不再持有着下载类 从字典里面移除
[download downloadCompleted:^(NSString *url) {
[self.downloadDic removeObjectForKey:url];
NSLog(@"下载完成");
}];
return download;
}
// 根据URL找到一个下载类
- (Download *)findDownloadWithURL:(NSString *)url
{
return self.downloadDic[url];
}
// 返回所有正在下载的类
- (NSArray *)allDownlod
{
return [self.downloadDic allValues];
}
其次是DownloadingManagment类,不知道原框架为什么会这么起,这其实就是一个Model累嘛,叫的人心都醉了。
@interface DownloadingManagment : NSObject<NSCoding>
@property(nonatomic, copy)NSString *resumeDataStr;
@property(nonatomic, copy)NSString *fileSize;
@property(nonatomic, copy)NSString *filePath;
@property(nonatomic, copy)NSString * progress;
@property(nonatomic, copy)NSString *url;
@property(nonatomic, copy)NSString * time;
@property(nonatomic, copy)NSString * DownLoadType;
@end
重头戏是Download 类,所有的网络有关的东西都在这个类里实现
最开始是初始化类
- (id)initWithURL:(NSString *)url
{
self = [super init];
if (self) {
// + (NSURLSessionConfiguration *)defaultSessionConfiguration;
// //返回标准配置,这实际上与NSURLConnection的网络协议栈是一样的,具有相同的共享NSHTTPCookieStorage,共享NSURLCache和共享NSURLCredentialStorage。
NSURLSessionConfiguration *config = [NSURLSessionConfiguration defaultSessionConfiguration];
//设置请求超时为10秒钟
config.timeoutIntervalForRequest = 10;
//在蜂窝网络情况下是否继续请求(上传或下载)
config.allowsCellularAccess = NO;
self.session = [NSURLSession sessionWithConfiguration:config delegate:self delegateQueue:[NSOperationQueue mainQueue]];
// 创建任务,把配置的信息,加到任务里,然后开始执行
self.task = [_session downloadTaskWithURL:[NSURL URLWithString:url]];
_url = url;
// 先去数据库中查找是否已经下载了 如果已经下载了 我们就把_isFirst赋值为yes 防止重复添加数据
_isFirst =[self findDownloadingWithURL:url];
if (_isFirst) {
// 如果已经下载了 我们更换我们的task
self.task = [self.session downloadTaskWithResumeData:[self resumeDataWithURL:url]];
}
}
return self;
}
里面涉及到NSURLSessionConfiguration,具体的可以查看http://www.cnblogs.com/liugengqun/p/5140296.html 博客写的很详细 如何配置
接下来断点续传主要功能,把下载了的
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
<key>NSURLSessionDownloadURL</key> //资源的 URL
<string>http://dlhjhx.xicp.net:13081/firmware/E302620170303.pkg</string>
<key>NSURLSessionResumeBytesReceived</key>//录下来了已经下载完成的字节数
<integer>68441</integer>
<key>NSURLSessionResumeCurrentRequest</key>//当前请求时的 NSURLRequest 对象
<data>
YnBsaXN0MDDUAQIDBAUGdXZYJHZlcnNpb25YJG9iamVjdHNZJGFyY2hpdmVyVCR0b3AS
//省略一万字
BcMF0AXTBeAF8gX1BhMAAAAAAAACAQAAAAAAAAB5AAAAAAAAAAAAAAAAAAAGFQ==
</data>
<key>NSURLSessionResumeEntityTag</key>
<string>W/"492894-1488511050000"</string>
<key>NSURLSessionResumeInfoTempFileName</key>//下载过程中临时文件所存储的位置,存储在应用程序 tmp 文件夹下
<string>CFNetworkDownload_kv1UKk.tmp</string>
<key>NSURLSessionResumeInfoVersion</key>
<integer>2</integer>
<key>NSURLSessionResumeOriginalRequest</key>//初始请求时的 NSURLRequest 对象
<data>
YnBsaXN0MDDUAQIDBAUGUFFYJHZlcnNpb25YJG9iamVjdHNZJGFyY2hpdmVyVCR0b3AS
//省略一万字
BAwEEQQeBCEELgRABEMEYQAAAAAAAAIBAAAAAAAAAFQAAAAAAAAAAAAAAAAAAARj
</data>
<key>NSURLSessionResumeServerDownloadDate</key>
<string>Fri, 03 Mar 2017 03:17:30 GMT</string>
</dict>
</plist>
每次启动的时候如果遇到未下载完成的任务就会替换任务Data中的已下载的字节,然后继续添加到任务重继续执行,这样就可以执行断点续传啦
/**
恢复下载替换resumeDataStr 中生于的下载的字节
@param url url description
@return return value description
*/
- (NSData *)resumeDataWithURL:(NSString *)url
{
// 1.找到下载中的model
DownloadingManagment *downloading = [self findDownloadingWithURL:url];
// 给progress赋初始值
_progress = downloading.progress;
NSLog(@"%@",downloading);
// 2.获取当前下载的文件
NSFileManager *fm = [NSFileManager defaultManager];
NSString *fileSize = [NSString stringWithFormat:@"%llu", [fm attributesOfItemAtPath:downloading.filePath error:nil].fileSize];
// 3.对resumeDataStr进行替换
downloading.resumeDataStr = [downloading.resumeDataStr stringByReplacingOccurrencesOfString:downloading.fileSize withString:fileSize];
// 4.生成NSData进行返回
return [downloading.resumeDataStr dataUsingEncoding:NSUTF8StringEncoding];
}
最后没啥可说的了,下载完成后转移数据啥的,这玩意关键要根据实际的业务需求来啦,剩下的GitHub上有例子,有兴趣的童鞋可以下载看一下,我这水平有限,也就是抛砖引玉,大神请绕行。GitHub Demo
最后感慨最近体弱多病呀,药加起来吃的是我N年的和呀,告诫大家要多强身健体呀,我这体会到身体是革命的本钱,没有好身体啥都搞不了,如配图呀,三月不努力,四月徒伤悲,五月就没脸见人啦。要个当一个能撑得起世界的男人