iOS多任务断点续传之"框架"封装

效果演示

demo6.gif

用法和demo:

https://github.com/xshenpan/iOSdemo.git

写它的原因

  • 学习了网络这一块之后没做过什么东西,感觉不踏实,于是将单任务版的断点续传做了加强,变成了多任务断点续传。虽然还是BUG不断,但是至少感觉踏实些了。
  • 以前学习过怎么用第三方框架,但是从没想过自己会写,于是想着既然要写个多任务的下载demo,为什么不把他封装一下呢?于是就开始去想怎么写,然后自己用“http file server”作为内网的服务器放了一些资源,然后慢慢写慢慢改看外界怎么使用方便,断断续续写写改改一个星期终于能够基本使用了。然后就将其分享出来,共同学习。
  • 当然,代码有许多地方欠缺考虑,经不住暴力测试
  • 作为一个初学者,代码中免不了许多BUG、考虑不周、思想错误、接口不合理等等问题。英语也比较烂,其中可能有各种句子不通,意义不对的位置。写这个小小的demo也是为了加强学习,希望大家能够对其中的各种错误轻喷

多任务下载怎么写

  • 怎么开头呢?

    • 既然是多任务,那就是单任务的组合,先把单任务写了。用的就是MJ老师视频里讲的单任务下载的方法,不带断点续传的单任务
  • 单任务写好了然后呢?

    • 多任务是对单任务下载的管理,那么单任务应该封装的简单易用。任务应该能开始/暂停/关闭,能读取下载进度,获得文件大小,能知道文件是否下载完成,是否下载出错。于是封装成了一个"XBDownloadTask"类,并提供了下面的额接口

typedef void(^XBCompleteBlock)(NSString *filePath, NSError *error);
typedef void(^XBDownloadProgressBlock)(NSInteger bytesRead, NSInteger totalBytesRead , NSInteger totalBytesExpectedToRead);

  • (void)pause;
  • (void)start;
  • (void)cancel;
  • (void)setProgressBlock:(XBDownloadProgressBlock)progressBlock;
  • (void)setCompleteBlock:(XBCompleteBlock)completeBlock;
  • (instancetype)downloadWithRequest:(NSURLRequest *)req andTempFileName:(NSString *)name;
  • 单任务封装好了,多任务要怎样做?
    • 首先是多个任务怎么保存起来?
      • 我用一个数组将所有的任务对象保存起来,像对列一样使用数组,就能实现先加进来的任务先开始任务,后加进来的任务后等待前面的任务完成,由于数组又具有随机访问能力,所以可以对任何地方的任务进行控制
    • 任务由谁管理呢?
      • 当然任务的管理不可能交给控制器,那样失去了封装的意义了。所以应该由一个统一的对象管理。一般来说,像浏览器还有一些下载软件,他们添加任务和管理任务不在同一个控制器,可能在多个地方添加任务,在同一个控制器管理任务。那么管理类应该做成单例模式,这样就能在不同的地方拿到统一管理对象进行添加任务,也能在管理任务的控制器中统一管理任务了
      • 所以将管理对象做成了一个单例模式的类 "XBDownloadManager",外界要方便对多任务进行管理,那么怎么去告诉管理器我需要控制哪个任务呢? 由于使用的是数组存储的任务对象,那么则可以很方便的使用索引操作任务对象。万一不想使用索引管理对象怎么办,所以在使用数组存储对象的时候同时也使用了字典对任务进行存储,这样既可以使用索引对任务进行控制(使用Tableview管理任务是,索引是比较方便的),也可以使用key对任务进行控制。我要获得任务详细信息怎么办? 代理的作用就体现出来了,使用一些方法去通知代理某个任务的就具体状态。 但是我不想知道任务具体信息怎么办?我只是添加一下任务,在多个控制器的右上角或什么地方显示一些任务的数量,这是一个一对多的问题,那么通知可以很好的解决这一问题。最终的接口如下:
static const CGFloat kManagerProgressUpdateInterval = 0.5;    //unit : second
static NSString * const kXBDownloadManagerNotification = @"XBDownloadManagerNotification";
static NSString * const kManagerNotificationTaskNumberKey = @"XBDownloadManagerTaskNumber";

@protocol XBDownloadManagerDelegate <NSObject>
@optional
//删除任务时调用,内部保证同一任务的其他代理方法会在该方法之前调用
- (void)managerDeleteTaskForKey:(NSString *)key atIndex:(NSInteger)idx;
//获得响应文件长度是调用
- (void)managerTaskFileLength:(NSInteger)fileLength forKey:(NSString *)key atIndex:(NSInteger)idx;
//进度和速度更新是调用,由kManagerProgressUpdateInterval控制
- (void)managerRefreshTaskProgress:(CGFloat)progress speed:(CGFloat)speed forKey:(NSString *)key atIndex:(NSInteger)idx;
//任务状态改变时调用
- (void)managerTaskStatusChanged:(XBDownloadTaskStatus)status forKey:(NSString *)key atIndex:(NSInteger)idx;
//任务完成时调用
- (void)managerTaskCompleteWithError:(NSError *)error forKey:(NSString *)key atIndex:(NSInteger)idx;
//添加任务,或设置代理时调用
- (void)managerAddTaskName:(NSString *)name andStatus:(XBDownloadTaskStatus)status fileLength:(NSInteger)length forKey:(NSString *)key atIndex:(NSInteger)idx;

@end

@interface XBDownloadManager : NSObject
/** 最大同时下载任务数,最大只能有10个 */
@property (nonatomic, assign) NSInteger maxDownloadTask;
/** 代理 */
@property (nonatomic, weak, readonly) id<XBDownloadManagerDelegate> delegate;
/** 任务数量 */
@property (nonatomic, assign, readonly) NSInteger taskNumber;


//控制方法
- (void)startAllDownloadTask;
- (void)pauseAllDownloadTask;
- (void)startWithIndex:(NSInteger)idx;
- (void)pauseWithIndex:(NSInteger)idx;
- (void)cancelWithIndex:(NSInteger)idx;
- (void)startWithKey:(NSString *)key;
- (void)pauseWithKey:(NSString *)key;
- (void)cancelWithKey:(NSString *)key;
- (void)reloadWithKey:(NSString *)key;
//查询任务
- (XBDownloadTaskInfo *)taskInfoWithIndex:(NSInteger)idx;
- (XBDownloadTaskInfo *)taskInfoWithKey:(NSString *)key;
//设置代理和代理执行的队列
- (void)setDelegate:(id<XBDownloadManagerDelegate>)delegate andDelegateQueue:(NSOperationQueue *)queue;
/** 添加一个下载任务,path=nil这默认在cache/xbdownload目录 路经以home开始, key=nil 则key = url.md5string */
- (NSString *)addDownloadTaskWithUrl:(NSString *)url andRelativePath:(NSString *)path taskKey:(NSString *)key taskExist:(void(^)(NSString *key))exist;

+ (instancetype)manager;

管理类怎么实现?

  • 断点续传怎么做?

    • 先前实现了单任务下载,但是那个单任务是不带断点续传的。简单的断点续传就是在请求头中设置一个 "Range:"字段,则服务器就会从你指定的地方传输数据。所以将不能断点续传单任务下载变成能够断点续传的单任务下载只需要改变请求头即可。
    • 所以下载的任务是改变传递给 "XBDownloadTask"类的请求头即可实现单任务下载。
  • 请求头怎么设置呢?

    • 假设现在有一个任务正在下载中,程序突然被中断了,由于"XBDownloadTask"使用的OutputStream类将数据写入文件,则下载了多少数据文件就是多大。那么再次启动该任务时,读取该任务对应的文件获取其大小生成一个带有"Range:bytes=xxx-"的请求头即可从上次中断的地方继续下载
  • 请求头好设置,那么我怎么知道从什么地方下载?我下载的文件名叫什么?

    • 因为程序被中段后,不可能叫用户再次去点击一下下载链接,所以我们不可能在程序中断之后再次获得链接,所有我们要在任务一添加进来的时候讲链接存储到磁盘中,当然要记录的不仅仅是一个url,还有用户指定的目录等等,此时可以在创建一个类去记录任务的下载状态信息,该类遵守NSCoding协议,将所有的任务信息记录在一个文件中,该类在里面叫做 "XBDownloadInfo"
  • 信息记录好之后,怎么对加入的任务进行控制,调度?

    • 因为要限制最大同时进行的任务数,所以用一个 maxDownloadTask记录最大的任务数,使用currentTask记录当前正在执行的任务数,通过比较当前任务数与最大任务数决定是否启动下一个任务,如果需要启动任务,那么从队尾开始向前遍历,找到一个处于等待状态的任务启动它。每当一个任务执行完都会尝试去启动一个等待任务,这样任务就能循环不断的执行下了。
//三个主要的调度方法
//尝试启动指定的任务
- (void)tryStartupTheTask:(XBDownloadTaskRecord *)taskRecord;
//尝试启动等待的任务
- (NSInteger)tryStartupWaitingTask;
//根据任务记录,创建下载任务
- (void)startupTaskWithRecord:(XBDownloadTaskRecord *)taskRecord;

最后

  • 我也不知道乱七八糟说了一堆把大概说清楚没有,表述的乱了点。
  • 还是作为新手还是希望能对其他新手有点帮助吧!
  • 修复了视频中第二次进入下载任务管理界面时文件大小不显示的BUG
  • 继续去学习新的知识了,暂且把它的bug放一放吧。

补充一点

  • 还有一种风格的下载界面的demo没写,就是要添加的任务和正在下载的任务在同一个界面,估计要等一久在写了
最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 203,271评论 5 476
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 85,275评论 2 380
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 150,151评论 0 336
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 54,550评论 1 273
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 63,553评论 5 365
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,559评论 1 281
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 37,924评论 3 395
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,580评论 0 257
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 40,826评论 1 297
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,578评论 2 320
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,661评论 1 329
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,363评论 4 318
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 38,940评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,926评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,156评论 1 259
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 42,872评论 2 349
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,391评论 2 342

推荐阅读更多精彩内容