// // RAUploadManager.m // test_autolayout // // Created by Ray on 02/05/2017. // Copyright © 2017 USAI. All rights reserved. // #import #import "RAUploadManager.h" #import "NetworkUtils.h" #import "RAUtils.h" NSString *const kUploadSetting = @"UploadSettingKey"; @interface RAUploadManager () @property (nonatomic,strong) NSTimer *retryTimer; @property (nonatomic,strong) NSMutableArray *retryArr; @end @implementation RAUploadManager /*! * Called by Reachability whenever status changes. */ - (void) reachabilityChanged:(NSNotification *)note { [self handleReachability:self.reach]; } - (void)handleReachability:(Reachability *)reachability { NetworkStatus netStatus = [reachability currentReachabilityStatus]; if (self.onlyWiFi && netStatus != ReachableViaWiFi) { DebugLog(@"not wifi & stop task"); [self.retryArr removeAllObjects]; [self stopAllTasks]; } } - (instancetype)init { if (self = [super init]) { /* Observe the kNetworkReachabilityChangedNotification. When that notification is posted, the method reachabilityChanged will be called. */ [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(reachabilityChanged:) name:kReachabilityChangedNotification object:nil]; NSString *remoteHostName = @"www.apple.com"; self.reach = [Reachability reachabilityWithHostName:remoteHostName]; [self.reach startNotifier]; [self handleReachability:self.reach]; self.queue_status = QueueStatusDefault; NSDictionary *param = [[NSUserDefaults standardUserDefaults] objectForKey:kUploadSetting]; if (param) { BOOL autoUpload = [[param objectForKey:@"auto_upload"] boolValue]; BOOL autoRmFinish = [[param objectForKey:@"auto_rm_finish"] boolValue]; BOOL autoRmErr = [[param objectForKey:@"auto_rm_error"] boolValue]; BOOL onlyWiFi = [[param objectForKey:@"only_wifi"] boolValue]; int retryCount = [[param objectForKey:@"retry_count"] intValue]; NSNumber *timeInterval = [param objectForKey:@"retry_time_interval"]; if (timeInterval != nil) { self.retryTimeInterval = [timeInterval intValue]; } else { self.retryTimeInterval = 300; } self.autoStart = autoUpload; // self.removeFinish = autoRmFinish; // self.removeError = autoRmErr; self.maxRetry = retryCount; self.onlyWiFi = onlyWiFi; } else { self.autoStart = YES; // self.removeFinish = YES; // self.removeError = NO; self.maxRetry = 5; self.onlyWiFi = NO; self.retryTimeInterval = 300; } self.newtaskStatus = TaskStatusWait; self.maxThread = 3; // self.autoStart = true; // self.removeError=true; // self.removeFinish = true; self.operation_queue = [NSOperationQueue new]; self.operation_queue.maxConcurrentOperationCount = self.maxThread; [self.operation_queue addObserver:self forKeyPath:@"operationCount" options:(NSKeyValueObservingOptionNew|NSKeyValueObservingOptionOld) context:@"queueFinished changed"]; //load task 要在监听arr_queue之前 [self loadTasks]; [self addObserver:self forKeyPath:@"arr_queue" options:(NSKeyValueObservingOptionNew|NSKeyValueObservingOptionOld) context:@"arr_queue changed"]; // self.arr_queue_lock = [NSLock new]; // [self.arr_queue_lock lock]; // [self.arr_queue_lock lock]; // [self.arr_queue_lock unlock]; // [self.arr_queue_lock unlock]; // self.backgroundColor = [UIColor clearColor]; } return self; } -(void) dealloc { [self destructRetryTimer]; [[NSNotificationCenter defaultCenter] removeObserver:self name:kReachabilityChangedNotification object:nil]; [self.operation_queue removeObserver:self forKeyPath:@"operationCount"]; [self.operation_queue removeObserver:self forKeyPath:@"arr_queue"]; } - (void)observeValueForKeyPath:(NSString *)keyPath ofObject:(id)object change:(NSDictionary *)change context:(void *)context { if([keyPath isEqualToString:@"operationCount"]) { // [self checkRetryOperations]; if(self.operation_queue.operationCount == 0 && self.retryArr.count == 0) { for(NSMutableDictionary* task in self.arr_queue) { if([task[@"status"] intValue] == TaskStatusError) { self.queue_status = QueueStatusFinishWithError; return; } } self.queue_status = QueueStatusFinish; } } if([keyPath isEqualToString:@"arr_queue"]) { NSArray* newqueue=[change objectForKey:NSKeyValueChangeNewKey]; NSArray* oldqueue=[change objectForKey:NSKeyValueChangeOldKey]; if([newqueue isKindOfClass:[NSNull class]]) newqueue=nil; if([oldqueue isKindOfClass:[NSNull class]]) oldqueue=nil; if(newqueue.count>oldqueue.count) { self.queue_status = QueueStatusAdd; return; } } } //-(void) upload:(NSString*) url //{ // while(self.activeThreadAtIndex // [ insertObjects:tasks atIndexes:indexSet]; } // if (self.delegate && [self.delegate respondsToSelector:@selector(uploadManager:didRemoveTasks:)]) { // [self.delegate uploadManager:self didRemoveTasks:tasks]; // } } -(void) removeTask:(NSMutableDictionary*) task { // [self.arr_queue addObject:task]; [self stopTask:task]; // 上传成功才删除文件 // NSFileManager* fmanager = [NSFileManager new]; // NSError* error = nil; // bool bdel=[fmanager removeItemAtPath:[self filePath:task] error:&error]; // if(!bdel) // DebugLog(@"file delete failed path:%@",[self filePath:task]); // [self.arr_queue_lock lock]; // DebugLog(@"lock"); // [[self mutableArrayValueForKey:@"arr_queue"] removeObject:task]; // DebugLog(@"unlock"); // [self.arr_queue_lock unlock]; // [self.arr_queue_lock lock]; // [[self mutableArrayValueForKey:@"arr_queue"] removeObject:task]; // [self.arr_queue_lock unlock]; @synchronized(self) { [[self mutableArrayValueForKey:@"arr_queue"] removeObject:task]; } // if (self.delegate && [self.delegate respondsToSelector:@selector(uploadManager:didRemoveTasks:)]) { // [self.delegate uploadManager:self didRemoveTasks:@[task]]; // } } //-(NSString*)filePath:(NSMutableDictionary*)task //{ // // NSString *path = [RAUtils appCacheDirectory]; // path= [path stringByAppendingPathComponent:task[@"path"]]; // path= [path stringByAppendingPathComponent:task[@"file"]]; // return path; //} -(void) saveTasks { NSUserDefaults *defaults =[NSUserDefaults standardUserDefaults]; [defaults setObject:self.arr_queue forKey:@"upload_queue"]; [defaults synchronize]; } -(void) loadTasks { [self stopAllTasks]; self.arr_queue = [NSMutableArray new]; NSMutableArray* arr = [[[NSUserDefaults standardUserDefaults] objectForKey:@"upload_queue"] mutableCopy]; // if(arr==nil) for(NSDictionary* task in arr) { NSMutableDictionary* newtask =[task mutableCopy]; [self.arr_queue addObject:newtask]; if(self.autoStart&&[task[@"status"] intValue]!=TaskStatusError&&[task[@"status"] intValue]!=TaskStatusFinish) [self startTask:newtask]; } // NSDictionary* ddd = arr[0] ; // NSMutableDictionary* eee = [ddd mutableCopy]; // // eee=[NSMutableDictionary dictionaryWithDictionary:ddd]; // if(arr!=nil) // self.arr_queue=arr; // else // self.arr_queue= [[NSMutableArray alloc] init]; // // for(__strong NSMutableDictionary* task in arr) // { // // task=[NSMutableDictionary dictionaryWithDictionary:task]; //// if([task[@"status"] intValue]==TaskStatusStart) //// task[@"status"]=[NSNumber numberWithInteger:TaskStatusWait]; // // NSMutableDictionary *new_task = [task mutableCopy]; // if(self.autoStart&&[task[@"status"] intValue]!=TaskStatusError&&[task[@"status"] intValue]!=TaskStatusFinish) // [self startTask:task]; // } } -(void) stopAllTasks { for(NSMutableDictionary* task in self.arr_queue) { [self stopTask:task]; } } -(void) stopTask:(NSMutableDictionary*) task { if([task[@"status"] intValue]!=TaskStatusStart&&[task[@"status"] intValue]!=TaskStatusWait) { task[@"operation"]=nil; return; // task 已停止,返回 } DebugLog(@"stop task"); RAUploadOperation* operation = task[@"operation"]; [operation cancel]; task[@"operation"]=nil; task[@"status"] = [NSNumber numberWithInt:TaskStatusStop] ; task[@"progress"] = [NSNumber numberWithDouble:0.0]; } -(void) startAllTasks { for(NSMutableDictionary* task in self.arr_queue) [self startTask:task]; } -(void) startTask:(NSMutableDictionary*) task { if (self.onlyWiFi && self.reach.currentReachabilityStatus != ReachableViaWiFi) { return; } /*__block*/ NSMutableDictionary* block_task = task; __weak typeof(self) weakSelf = self; if([task[@"status"] intValue]==TaskStatusStart||[task[@"status"] intValue]==TaskStatusWait) return; // task 已在执行队列,返回 RAUploadOperation* operation = [[RAUploadOperation alloc] initWithTaskinfo:task retry:self.maxRetry]; [operation setCompletionBlock:^{ switch ([block_task[@"status"] intValue]) { case TaskStatusFinish: { // if(weakSelf.removeFinish) // [self removeTask:block_task]; } break; case TaskStatusError: { // if(weakSelf.removeError) // [self removeTask:block_task]; } break; default: break; } }]; task[@"msg"] = nil; task[@"operation"] = operation; task[@"status"] = [NSNumber numberWithInt:self.newtaskStatus] ; [self.operation_queue addOperation:operation]; } //- (void) uploadTask:(NSMutableDictionary*) task url:(NSString*)url{ // //// __weak typeof(self) weakself = self; // // // // //} - (BOOL)canUpload { if (self.onlyWiFi && self.reach.currentReachabilityStatus != ReachableViaWiFi) { return NO; } return YES; } #pragma mark - Retry - (NSMutableArray *)retryArr { if (!_retryArr) { _retryArr = [NSMutableArray array]; } return _retryArr; } - (void)addRetryOperation:(RAUploadOperation *)operation { if (self.retryTimer == nil) { // self.retryTimer = [NSTimer scheduledTimerWithTimeInterval:60 target:self selector:@selector(checkRetryOperations) userInfo:nil repeats:YES]; self.retryTimer = [NSTimer timerWithTimeInterval:60 target:self selector:@selector(checkRetryOperations) userInfo:nil repeats:YES]; [[NSRunLoop mainRunLoop] addTimer:self.retryTimer forMode:NSRunLoopCommonModes]; } [self.retryArr addObject:operation]; // 升序 [self.retryArr sortUsingComparator:^NSComparisonResult(RAUploadOperation* _Nonnull operation1, RAUploadOperation* _Nonnull opertation2) { if (operation1.theRestOfWaitTimeInterval < opertation2.theRestOfWaitTimeInterval) { return NSOrderedAscending; } else { return NSOrderedDescending; } }]; } - (void)checkRetryOperations { // 检查是否有需要重试的任务 if (self.retryArr.count == 0) { return; } // 检查Wi-Fi Only if (![self canUpload]) { return; } NSMutableArray *nextStartOperations = [NSMutableArray array]; // // 检查当前上传队列等待数量是否小于最大并发数 // if (self.operation_queue.operationCount < self.maxThread) { // //// // 根据最大并发数将剩余Operation全部添加进去 //// NSInteger minCount = MIN(self.retryArr.count, self.maxThread - self.operation_queue.operationCount); // for (int i = 0; i < self.retryArr.count; i++) { // RAUploadOperation *operation = [self.retryArr objectAtIndex:i]; // operation.queuePriority = NSOperationQueuePriorityVeryHigh; // [self.operation_queue addOperation:operation]; // [nextStartOperations addObject:operation]; // } // // } else { // 调度等待时间到达的Operation __weak typeof(self) weakSelf = self; [self.retryArr enumerateObjectsUsingBlock:^(RAUploadOperation * _Nonnull operation, NSUInteger idx, BOOL * _Nonnull stop) { if (operation.theRestOfWaitTimeInterval <= 0) { operation.queuePriority = NSOperationQueuePriorityVeryHigh; [weakSelf.operation_queue addOperation:operation]; [nextStartOperations addObject:operation]; } }]; // } [self.retryArr removeObjectsInArray:nextStartOperations]; // if (self.retryArr.count == 0) { // [self destructRetryTimer]; // } } - (void)destructRetryTimer { if (self.retryTimer != nil) { [self.retryTimer setFireDate:[NSDate distantFuture]]; [self.retryTimer invalidate]; self.retryTimer = nil; } } @end