继续浏览精彩内容
慕课网APP
程序员的梦工厂
打开
继续
感谢您的支持,我会继续努力的
赞赏金额会直接到老师账户
将二维码发送给自己后长按识别
微信支付
支付宝支付

用NSURLConnection封装一个断点续传后台下载的轻量级下载工具_part2

CGPointZero
关注TA
已关注
手记 9
粉丝 1
获赞 92

下载管理类 FGGDownloadManager.h

首先要导入下载类:#import "FGGDownloader.h"
单例接口

+(instancetype)shredManager;

同样有添加下载任务的接口:

/**
 *  断点下载
 *
 *  @param urlString        下载的链接
 *  @param destinationPath  下载的文件的保存路径
 *  @param  process         下载过程中回调的代码块,会多次调用
 *  @param  completion      下载完成回调的代码块
 *  @param  failure         下载失败的回调代码块
 */
-(void)downloadWithUrlString:(NSString *)urlString
                      toPath:(NSString *)destinationPath
                     process:(ProcessHandle)process
                  completion:(CompletionHandle)completion
                     failure:(FailureHandle)failure;

管理类自然可以暂停指定的某个下载,根据某个下载链接url,暂停某个下载

/**
 *  暂停下载
 *
 *  @param url 下载的链接
 */
-(void)cancelDownloadTask:(NSString *)url;

管理类可以暂停所有下载任务:

/**
 *  暂停所有下载
 */
-(void)cancelAllTasks;

管理类可以彻底移除某个下载任务:

/**
 *  彻底移除下载任务
 *
 *  @param url  下载链接
 *  @param path 文件路径
 */
-(void)removeForUrl:(NSString *)url file:(NSString *)path;

当然管理类可以根据下载链接url获取上一次的下载进度:

/**
 *  获取上一次的下载进度
 *
 *  @param url 下载链接
 *
 *  @return 下载进度
 */
-(float)lastProgress:(NSString *)url;

以及获取文件大小信息:

/**
 *  获取文件已下载的大小和总大小,格式为:已经下载的大小/文件总大小,如:12.00M/100.00M。
 *
 *  @param url 下载链接
 *
 *  @return 有文件大小及总大小组成的字符串
 */
-(NSString *)filesSize:(NSString *)url;

管理类的实现 FGGDownloadManager.m

  • 在这里,我们搞了一个队列,设置了同时最多允许多少个下载任务。
  • 超过最大下载任务后,添加下载任务将会被添加进入下载任务队列,处于等待模式。
  • 当当前正在进行的下载任务数小于允许最大同时下载任务数时,队列中的一个下载任务出列(遵循先入列的先出列)。

根据逻辑,我定制了以下成员变量

@implementation FGGDownloadManager{

    NSMutableDictionary         *_taskDict;
    /**
     *  排队对列
     */
    NSMutableArray              *_queue;
    /**
     *  后台进程id
     */
    UIBackgroundTaskIdentifier  _backgroudTaskId;
}

单例方法:

+(instancetype)shredManager
{
    static dispatch_once_t onceToken;
    dispatch_once(&onceToken, ^{
        mgr=[[FGGDownloadManager alloc]init];
    });
    return mgr;
}

初始化的时候,注册app进入后台,app会到前台,被终结等,以及系统内存不足的通知:

-(instancetype)init{

    if(self=[super init]){

        _taskDict=[NSMutableDictionary dictionary];
        _queue=[NSMutableArray array];
        _backgroudTaskId=UIBackgroundTaskInvalid;
        //注册系统内存不足的通知
        [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(systemSpaceInsufficient:) name:FGGInsufficientSystemSpaceNotification object:nil];
        //注册程序下载完成的通知
        [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(downloadTaskDidFinishDownloading:) name:FGGDownloadTaskDidFinishDownloadingNotification object:nil];
        //注册程序即将失去焦点的通知
        [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(downloadTaskWillResign:) name:UIApplicationWillResignActiveNotification object:nil];
        //注册程序获得焦点的通知
        [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(downloadTaskDidBecomActive:) name:UIApplicationDidBecomeActiveNotification object:nil];
        //注册程序即将被终结的通知
        [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(downloadTaskWillBeTerminate:) name:UIApplicationWillTerminateNotification object:nil];

    }
    return self;
}

收到系统内存不足的通知时,取消下载:

/**
 *  收到系统存储空间不足的通知调用的方法
 *
 *  @param sender 系统存储空间不足的通知
 */
-(void)systemSpaceInsufficient:(NSNotification *)sender{

    NSString *urlString=[sender.userInfo objectForKey:@"urlString"];
    [[FGGDownloadManager shredManager] cancelDownloadTask:urlString];
}

程序即将失去焦点,开启后台:

/**
 *  收到程序即将失去焦点的通知,开启后台运行
 *
 *  @param sender 通知
 */
-(void)downloadTaskWillResign:(NSNotification *)sender{

    if(_taskDict.count>0){

        _backgroudTaskId=[[UIApplication sharedApplication] beginBackgroundTaskWithExpirationHandler:^{

        }];
    }
}

程序重新获得焦点时,关闭后台:

/**
 *  收到程序重新得到焦点的通知,关闭后台
 *
 *  @param sender 通知
 */
-(void)downloadTaskDidBecomActive:(NSNotification *)sender{

    if(_backgroudTaskId!=UIBackgroundTaskInvalid){

        [[UIApplication sharedApplication] endBackgroundTask:_backgroudTaskId];
        _backgroudTaskId=UIBackgroundTaskInvalid;
    }
}

程序即将被终结时,取消所有下载任务:

/**
 *  程序将要结束时,取消下载
 *
 *  @param sender 通知
 */
-(void)downloadTaskWillBeTerminate:(NSNotification *)sender{

    [[FGGDownloadManager shredManager] cancelAllTasks];
}

收到下载完成的通知时,从排队队列中取出一个任务出列:

/**
 *  下载完成通知调用的方法
 *
 *  @param sender 通知
 */
-(void)downloadTaskDidFinishDownloading:(NSNotification *)sender{

    //下载完成后,从任务列表中移除下载任务,若总任务数小于最大同时下载任务数,
    //则从排队对列中取出一个任务,进入下载
    NSString *urlString=[sender.userInfo objectForKey:@"urlString"];
    [_taskDict removeObjectForKey:urlString];
    if(_taskDict.count<kFGGDwonloadMaxTaskCount){

        if(_queue.count>0){

            NSDictionary *first=[_queue objectAtIndex:0];

            [self downloadWithUrlString:first[@"urlString"]
                                 toPath:first[@"destinationPath"]
                                process:first[@"process"]
                             completion:first[@"completion"]
                                failure:first[@"failure"]];
            //从排队对列中移除一个下载任务
            [_queue removeObjectAtIndex:0];
        }
    }
}

添加下载任务:判断是否超过允许的最大的并发任务数,若大于,则进入队列派对,反之则搞一个下载类去下载这个任务:

-(void)downloadWithUrlString:(NSString *)urlString toPath:(NSString *)destinationPath process:(ProcessHandle)process completion:(CompletionHandle)completion failure:(FailureHandle)failure{

    //若同时下载的任务数超过最大同时下载任务数,
    //则把下载任务存入对列,在下载完成后,自动进入下载。
    if(_taskDict.count>=kFGGDwonloadMaxTaskCount){

        NSDictionary *dict=@{@"urlString":urlString,
                             @"destinationPath":destinationPath,
                             @"process":process,
                             @"completion":completion,
                             @"failure":failure};
        [_queue addObject:dict];

        return;
    }
    FGGDownloader *downloader=[FGGDownloader downloader];
    @synchronized (self) {
        [_taskDict setObject:downloader forKey:urlString];
    }
    [downloader downloadWithUrlString:urlString
                               toPath:destinationPath
                              process:process
                           completion:completion
                              failure:failure];
}

取消下载任务:

/**
 *  取消下载任务
 *
 *  @param url 下载的链接
 */
-(void)cancelDownloadTask:(NSString *)url{

    FGGDownloader *downloader=[_taskDict objectForKey:url];
    [downloader cancel];
    @synchronized (self) {
        [_taskDict removeObjectForKey:url];
    }
    if(_queue.count>0){

        NSDictionary *first=[_queue objectAtIndex:0];

        [self downloadWithUrlString:first[@"urlString"]
                             toPath:first[@"destinationPath"]
                            process:first[@"process"]
                         completion:first[@"completion"]
                            failure:first[@"failure"]];
        //从排队对列中移除一个下载任务
        [_queue removeObjectAtIndex:0];
    }
}

取消所有下载任务:

-(void)cancelAllTasks{

    [_taskDict enumerateKeysAndObjectsUsingBlock:^(id key, id obj, BOOL *stop) {
        FGGDownloader *downloader=obj;
        [downloader cancel];
        [_taskDict removeObjectForKey:key];
    }];
}

彻底移除下载任务:

/**
 *  彻底移除下载任务
 *
 *  @param url  下载链接
 *  @param path 文件路径
 */
-(void)removeForUrl:(NSString *)url file:(NSString *)path{

    FGGDownloader *downloader=[_taskDict objectForKey:url];
    if(downloader){
        [downloader cancel];
    }
    @synchronized (self) {
        [_taskDict removeObjectForKey:url];
    }
    NSUserDefaults *usd=[NSUserDefaults standardUserDefaults];
    NSString *totalLebgthKey=[NSString stringWithFormat:@"%@totalLength",url];
    NSString *progressKey=[NSString stringWithFormat:@"%@progress",url];
    [usd removeObjectForKey:totalLebgthKey];
    [usd removeObjectForKey:progressKey];
    [usd synchronize];

    NSFileManager *fileManager=[NSFileManager defaultManager];
    BOOL fileExist=[fileManager fileExistsAtPath:path];
    if(fileExist){

        [fileManager removeItemAtPath:path error:nil];
    }
}

根据url获取上次下载进度:

-(float)lastProgress:(NSString *)url{

    return [FGGDownloader lastProgress:url];
}

根据url获取上次下载大小:

-(NSString *)filesSize:(NSString *)url{

    return [FGGDownloader filesSize:url];
}

最后在销毁的时候,移除通知:

-(void)dealloc{

    [[NSNotificationCenter defaultCenter] removeObserver:self];
}

最后附上我的GitHub代码地址:FGGDownloader,欢迎pull request和star ~

打开App,阅读手记
1人推荐
发表评论
随时随地看视频慕课网APP