// // mixDownloadQueue.m // 双子星云手机 // // Created by xd h on 2024/6/27. // #import "mixDownloadQueue.h" #import "mixDownloadManager.h" #import "mixDownloadCacheManager.h" #import "mixDownloadSession.h" #import "NSURLSession+mixDownloadTask.h" @interface mixDownloadQueue () // 列队管理集合 @property (nonatomic,strong) NSMutableArray *operations; @end @implementation mixDownloadQueue - (instancetype)init { if (self = [super init]) { _maxCount = 1; [self registeNotification]; } return self; } - (void)dealloc { [[NSNotificationCenter defaultCenter] removeObserver:self]; } - (void)didResiveDownloadFileCompete:(NSNotification *)noti { mixDownloadOperation *operation = noti.object; if (operation) { [self.operations removeObject:operation]; } } - (NSMutableArray *)getOperationDoing { NSMutableArray *doingArray = [NSMutableArray array]; for (mixDownloadOperation *operation in self.operations) { if (operation.downloadState == DownloadStateDoing) { [doingArray addObject:operation]; } } return doingArray; } - (NSMutableArray *)getOperationWaiting { NSMutableArray *waitingArray = [NSMutableArray array]; [self.operations enumerateObjectsUsingBlock:^(mixDownloadOperation * _Nonnull obj, NSUInteger idx, BOOL * _Nonnull stop) { mixDownloadOperation *operation = (mixDownloadOperation *)obj; if (operation.downloadState == DownloadStateWaiting) { [waitingArray addObject:operation]; } }]; return waitingArray; } #pragma mark - handle Out operations - (void)addDownloadWithSession:(NSURLSession *)session URL:(NSURL *)url begin:(void(^)(NSString *))begin progress:(void(^)(NSInteger,NSInteger))progress complete:(void(^)(NSDictionary *,NSError *))complet { // 获取operation对象 mixDownloadOperation *operation = [self operationWithUrl:url.absoluteString]; if (operation == nil) { // 之前不存在此任务 operation = [[mixDownloadOperation alloc] initWith:url.absoluteString session:session]; if (operation == nil) { // 没有下载任务代表已下载完成 NSDictionary *fileInfo = [mixDownloadCacheManager queryFileInfoWithUrl:url.absoluteString]; if (fileInfo && complet) { complet(fileInfo,nil); }else { complet(nil,[NSError errorWithDomain:@"构建下载任务失败" code:-1 userInfo:nil]); } return; } [self.operations addObject:operation]; } if ([self getOperationDoing].count < self.maxCount) { // 下载中任务数少于最大任务限制 [operation.dataTask resume]; [self operationStartWithOperation:operation]; }else { // 下载中任务数大于最大任务限制 // [operation.dataTask suspend]; [self operationWaitingWithOperation:operation]; HLog(@"下载中任务数等于最大任务限制:%zd",[self getOperationDoing].count); } } - (void)operateDownloadWithUrl:(NSString *)url session:(NSURLSession *)session handle:(DownloadHandleType)handle { // 1、任务列表里取任务 mixDownloadOperation *operation = [self operationWithUrl:url]; HLog(@"operation.dataTask 1:%@",operation.dataTask); // 2、本地plist文件里提取的任务 if (!operation) { NSDictionary *dict = [mixDownloadCacheManager queryFileInfoWithUrl:url]; operation = [mixDownloadOperation mj_objectWithKeyValues:dict]; } HLog(@"operation.dataTask 2:%@",operation.dataTask); // 3、本地plist文件里提取的任务不存在dataTask if (!operation.dataTask && operation.currentSize != operation.totalSize) { operation.dataTask = [self.session mix_downloadDataTaskWithURLString:operation.url startSize:operation.currentSize]; [self.operations addObject:operation]; } HLog(@"operation.dataTask 3:%@",operation.dataTask); if (operation) { switch (handle) { case DownloadHandleTypeStart: switch (operation.dataTask.state) { case NSURLSessionTaskStateRunning: HLog(@"NSURLSessionTaskStateRunning"); break; case NSURLSessionTaskStateSuspended: HLog(@"NSURLSessionTaskStateSuspended"); //operation.dataTask = [self.session mix_downloadDataTaskWithURLString:operation.url startSize:operation.currentSize]; break; case NSURLSessionTaskStateCanceling: HLog(@"NSURLSessionTaskStateCanceling"); operation.dataTask = [self.session mix_downloadDataTaskWithURLString:operation.url startSize:operation.currentSize]; break; case NSURLSessionTaskStateCompleted: HLog(@"NSURLSessionTaskStateCompleted"); break; default: break; } if ([self getOperationDoing].count >= self.maxCount) { // 下载中任务数超过最大任务限制 HLog(@"下载中的任务数超过最大限制") // [operation.dataTask suspend]; // 暂停 [self operationWaitingWithOperation:operation]; return; } HLog(@"operation.dataTask resume:%@",operation.dataTask); [operation.dataTask resume]; // 开始 [self operationStartWithOperation:operation]; break; case DownloadHandleTypeSuspend: HLog(@"operation.dataTask suspend:%@",operation.dataTask); //[operation.dataTask suspend]; // 暂停 [operation.dataTask cancel]; // 暂停 [self operationSuspendWithOperation:operation]; //检测是否有等待下载的任务 有就开启下一个 [self taskExeEnd:nil]; break; case DownloadHandleTypeCancel: if (operation.dataTask) { // 任务列表删除任务 [operation.dataTask cancel]; // 取消 [self.operations removeObject:operation]; } [mixDownloadCacheManager deleteFileWithUrl:url]; // plist删除任务 [self operationDeleteWithOperation:operation]; break; } } } - (void)suspendAllTasksWithSession:(NSURLSession *)session { // 暂停所有的任务 [self.operations enumerateObjectsUsingBlock:^(mixDownloadOperation * _Nonnull obj, NSUInteger idx, BOOL * _Nonnull stop) { mixDownloadOperation *operation = (mixDownloadOperation *)obj; if (operation.currentSize != operation.totalSize || operation.currentSize ==0) { // 暂停未完成的下载任务 if (!operation.dataTask) { // 给plist里的任务添加dataTask 添加到operations operation.dataTask = [self.session mix_downloadDataTaskWithURLString:operation.url startSize:operation.currentSize]; } //[operation.dataTask suspend]; [operation.dataTask cancel]; [self operationSuspendWithOperation:operation]; }else { HLog(@"已完成的任务"); } }]; } - (void)startAllTasksWithSession:(NSURLSession *)session { // 开始所有的任务 [self.operations enumerateObjectsUsingBlock:^(mixDownloadOperation * _Nonnull obj, NSUInteger idx, BOOL * _Nonnull stop) { mixDownloadOperation *operation = (mixDownloadOperation *)obj; //if (!operation.dataTask) { // 给plist里的任务添加dataTask 添加到operations operation.dataTask = [self.session mix_downloadDataTaskWithURLString:operation.url startSize:operation.currentSize]; //} if ([self getOperationDoing].count < self.maxCount) { // 下载中任务数少于最大任务限制 HLog(@"getOperationDoing %@",operation.fileName); switch (operation.dataTask.state) { case NSURLSessionTaskStateRunning: HLog(@"NSURLSessionTaskStateRunning"); break; case NSURLSessionTaskStateSuspended: HLog(@"NSURLSessionTaskStateSuspended"); break; case NSURLSessionTaskStateCanceling: HLog(@"NSURLSessionTaskStateCanceling"); operation.dataTask = [self.session mix_downloadDataTaskWithURLString:operation.url startSize:operation.currentSize]; break; case NSURLSessionTaskStateCompleted: HLog(@"NSURLSessionTaskStateCompleted"); break; default: break; } [operation.dataTask resume]; // 开始 [self operationStartWithOperation:operation]; }else { // 下载中任务数大于最大任务限制 // [operation.dataTask suspend]; [self operationWaitingWithOperation:operation]; HLog(@"下载中任务数等于最大任务限制:%zd",[self getOperationDoing].count); } }]; } #pragma mark-监听通知 - (void)registeNotification { // 监听完成通知 [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(didResiveDownloadFileCompete:) name:mixDownloadCompleteNoti object:nil]; [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(taskExeEnd:) name:mixDownloadTaskExeEnd object:nil]; // [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(taskExeEnd:) name:SGDownloadTaskExeError object:nil]; [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(taskExeEnd:) name:mixDownloadTaskExeSuspend object:nil]; // [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(taskExeEnd:) name:SGDownloadTaskExeDelete object:nil]; } - (void)taskExeEnd:(NSNotification *)notification { NSMutableArray *operationWaiting = [self getOperationWaiting]; [operationWaiting enumerateObjectsUsingBlock:^(id _Nonnull obj, NSUInteger idx, BOOL * _Nonnull stop) { mixDownloadOperation *operation = obj; if ([self getOperationDoing].count < self.maxCount) { [self operateDownloadWithUrl:operation.url session:self.session handle:DownloadHandleTypeStart]; }else { *stop = YES; } }]; } #pragma mark 发送通知 // 暂停某一个operation 保存本地 通知外界 - (void)operationSuspendWithOperation:(mixDownloadOperation *)operation { operation.downloadState = DownloadStateSuspended; [mixDownloadCacheManager saveFileInfoWithDict:[operation downLoadInfoWithFinished:NO]]; //没有必要再操作的时候通知外面 //[[NSNotificationCenter defaultCenter] postNotificationName:SGDownloadTaskExeSuspend object:nil userInfo:@{@"operation" : operation}]; } // 重启某一个operation 保存本地 通知外界 - (void)operationStartWithOperation:(mixDownloadOperation *)operation { operation.downloadState = DownloadStateDoing; [mixDownloadCacheManager saveFileInfoWithDict:[operation downLoadInfoWithFinished:NO]]; //没有必要再操作的时候通知外面 //[[NSNotificationCenter defaultCenter] postNotificationName:SGDownloadTaskExeing object:nil userInfo:@{@"operation" : operation}]; } // 等待某一个operation 保存本地 通知外界 - (void)operationWaitingWithOperation:(mixDownloadOperation *)operation { operation.downloadState = DownloadStateWaiting; [mixDownloadCacheManager saveFileInfoWithDict:[operation downLoadInfoWithFinished:NO]]; //没有必要再操作的时候通知外面 //[[NSNotificationCenter defaultCenter] postNotificationName:SGDownloadTaskExeSuspend object:nil userInfo:@{@"operation" : operation}]; } // 删除某一个operation 保存本地 通知外界 - (void)operationDeleteWithOperation:(mixDownloadOperation *)operation { //1. 任务删除了为什么不删除本地数据? //[[NSNotificationCenter defaultCenter] postNotificationName:SGDownloadTaskExeDelete object:nil userInfo:nil]; } #pragma mark - handle download - (void)dataTask:(NSURLSessionDataTask *)dataTask didReceiveResponse:(NSURLResponse *)response { [[self oprationWithDataTask:dataTask] operateWithResponse:response]; } - (void)dataTask:(NSURLSessionDataTask *)dataTask didReceiveData:(NSData *)data { [[self oprationWithDataTask:dataTask] operateWithReceivingData:data]; } - (void)task:(NSURLSessionTask *)task didCompleteWithError:(NSError *)error { [[self oprationWithDataTask:task] operateWithComplete:error]; } #pragma mark - query operation - (mixDownloadOperation *)operationWithUrl:(NSString *)url{ __block mixDownloadOperation *operation = nil; [self.operations enumerateObjectsUsingBlock:^(mixDownloadOperation * _Nonnull obj, NSUInteger idx, BOOL * _Nonnull stop) { if ([obj.url isEqualToString:url]) { operation = obj; *stop = YES; } }]; return operation; } // 寻找operation - (mixDownloadOperation *)oprationWithDataTask:(NSURLSessionTask *)dataTask { __block mixDownloadOperation *operation = nil; [self.operations enumerateObjectsUsingBlock:^(mixDownloadOperation * _Nonnull obj, NSUInteger idx, BOOL * _Nonnull stop) { if (obj.dataTask == dataTask) { operation = obj; *stop = YES; } }]; return operation; } #pragma mark - lazy load - (NSMutableArray *)operations { if (!_operations) { _operations = [NSMutableArray array]; [self addOperationsFromPlist]; } return _operations; } - (void)addOperationsFromPlist { [self.operations removeAllObjects]; NSMutableArray *operations = [[mixDownloadManager shareManager] getAllOperation]; for (mixDownloadOperation *operationPlist in operations) { // 1、任务列表里取任务 mixDownloadOperation *operation = [self operationWithUrl:operationPlist.url]; // 2、本地plist文件里提取的任务 if (!operation) { NSDictionary *dict = [mixDownloadCacheManager queryFileInfoWithUrl:operationPlist.url]; operation = [mixDownloadOperation mj_objectWithKeyValues:dict]; } // 3、本地plist文件里提取的任务不存在dataTask if(operation){ if (!operation.dataTask) { operation.dataTask = [self.session mix_downloadDataTaskWithURLString:operation.url startSize:operation.currentSize]; operation.handle = [NSFileHandle fileHandleForWritingAtPath:operation.fullPath]; [self.operations addObject:operation]; } } else{ HLog(@"%@ --- 没找到",operationPlist.url) } } } @end