123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284 |
- //
- // RQDownloadOperation.m
- // TEST
- //
- // Created by 张嵘 on 2018/10/22.
- // Copyright © 2018 张嵘. All rights reserved.
- //
- #import "RQDownloadOperation.h"
- #import "RQDownloadHeader.h"
- #define kKVOBlock(KEYPATH,BLOCK)\
- [self willChangeValueForKey:KEYPATH];\
- BLOCK();\
- [self didChangeValueForKey:KEYPATH];
- @interface RQDownloadOperation (){
-
- BOOL _executing;
- BOOL _finished;
- }
- @property (nonatomic, strong) NSLock *lock;
- @property (nonatomic, assign) BOOL taskIsFinished;
- @end
- static const NSTimeInterval kTimeoutInterval = 60;
- static NSString * const kIsExecuting = @"isExecuting";
- static NSString * const kIsCancelled = @"isCancelled";
- static NSString * const kIsFinished = @"isFinished";
- @implementation RQDownloadOperation
- MJCodingImplementation
- - (instancetype)initWithDownloadModel:(RQDownloadModel *)downloadModel andSession:(NSURLSession *)session{
- self = [super init];
- if (self) {
- self.downloadModel = downloadModel;
- self.session = session;
- self.downloadModel.status = RQDownloadStatus_Waiting;
- }
- return self;
- }
- - (void)dealloc{
- NSLog(@"任务已销毁");
- }
- #pragma mark - Public Method
- - (void)startRequest{
-
- //已下载完成 || 任务未就绪 --> 则直接返回
- if (self.downloadModel.isFinished || !self.isReady) {
- return;
- }
- // 创建请求
- NSURL *url = [NSURL URLWithString:self.downloadModel.urlString];
- NSMutableURLRequest *request = [NSMutableURLRequest requestWithURL:url cachePolicy:NSURLRequestUseProtocolCachePolicy timeoutInterval:kTimeoutInterval];
-
- // 设置请求头
- NSString *range = [NSString stringWithFormat:@"bytes=%d-", self.downloadModel.fileDownloadSize];
- [request setValue:range forHTTPHeaderField:@"Range"];
-
- if(!self.downloadTask){
- self.downloadTask = [self.session dataTaskWithRequest:request];
- }
-
- self.downloadTask.downloadModel = self.downloadModel;
- [self addObserver];
-
- [self.downloadTask resume];
- }
- // 进行检索获取Key
- - (BOOL)observerKeyPath:(NSString *)key observer:(id )observer{
-
- id info = self.downloadTask.observationInfo;
- NSArray *array = [info valueForKey:@"_observances"];
- for (id objc in array) {
- id Properties = [objc valueForKeyPath:@"_property"];
- id newObserver = [objc valueForKeyPath:@"_observer"];
-
- NSString *keyPath = [Properties valueForKeyPath:@"_keyPath"];
- if ([key isEqualToString:keyPath] && [newObserver isEqual:observer]) {
- return YES;
- }
- }
- return NO;
- }
- - (void)addObserver{
-
- if (![self observerKeyPath:@"state" observer:self]) {
- [self.downloadTask addObserver:self
- forKeyPath:@"state"
- options:NSKeyValueObservingOptionNew
- context:nil];
- }
- }
- - (void)removeObserver{
-
- if ([self observerKeyPath:@"state" observer:self]){
- [self.downloadTask removeObserver:self forKeyPath:@"state"];
- }
- }
- /** 挂起任务 */
- - (void)suspend{
-
- NSLog(@"%@: currentThread = %@", NSStringFromSelector(_cmd), [NSThread currentThread]);
-
- kKVOBlock(kIsExecuting, ^{
- [self.downloadTask suspend];
- _executing = NO;
- });
- }
- /** 开始执行任务 */
- - (void)startExcuting{
-
- NSLog(@"%@: currentThread = %@", NSStringFromSelector(_cmd), [NSThread currentThread]);
-
- kKVOBlock(kIsExecuting, ^{
- [self startRequest];
- _executing = YES;
- });
- }
- /** 开始执行任务 */
- - (void)resume{
-
- //等待中的任务交给队列调度
- if (self.downloadModel.status == RQDownloadStatus_Waiting)
- return;
-
- kKVOBlock(kIsExecuting, ^{
- [self startRequest];
- _executing = YES;
- });
- }
- /** 任务已完成 */
- - (void)completeOperation{
-
- [self willChangeValueForKey:kIsFinished];
- [self willChangeValueForKey:kIsExecuting];
-
- _executing = NO;
- _finished = YES;
-
- [self didChangeValueForKey:kIsExecuting];
- [self didChangeValueForKey:kIsFinished];
- }
- - (void)observeValueForKeyPath:(NSString *)keyPath ofObject:(id)object change:(NSDictionary<NSString *,id> *)change context:(void *)context{
-
- if ([keyPath isEqualToString:@"state"]) {
-
- NSInteger newState = [[change objectForKey:@"new"] integerValue];
- NSInteger oldState = [[change objectForKey:@"old"] integerValue];
-
- switch (newState) {
- case NSURLSessionTaskStateSuspended:
- self.downloadModel.status = RQDownloadStatus_Suspended;
- //为进行任务管理 暂停任务后 直接取消
- [self cancel];
- break;
- case NSURLSessionTaskStateCompleted:{
- if (self.downloadModel.isFinished) {
- self.downloadModel.status = RQDownloadStatus_Completed;
- [self cancel];
- }else{
- if (self.downloadModel.status == RQDownloadStatus_Suspended) {
- }else{// 下载失败
- self.downloadModel.status = RQDownloadStatus_Failed;
- }
- }
- }break;
- case NSURLSessionTaskStateRunning:
- self.downloadModel.status = RQDownloadStatus_Running;
- break;
- case NSURLSessionTaskStateCanceling:
- self.taskIsFinished = YES;
- break;
- default:
- break;
- }
-
- if (newState != oldState) {
- if (self.downloadStatusChangedBlock) {
- self.downloadStatusChangedBlock();
- }
- }
- }
- }
- #pragma mark - Override Methods
- - (void)start{
-
- self.lock = [[NSLock alloc] init];
- [self.lock lock];
- //重写start方法时,要做好isCannelled的判断
- if ([self isCancelled]){
- //若已取消则设置状态已完成
- kKVOBlock(kIsFinished, ^{
- _finished = YES;
- });
- return;
- }
-
- kKVOBlock(kIsExecuting, ^{
- _executing = YES;
- });
-
- //未取消则调用main方法来执行任务
- //经测试 加入operationQueue中后会自动开启新的线程执行 无需手动开启
- [NSThread currentThread].name = self.downloadModel.downloadDesc;
- [NSThread mainThread].name = @"主线程";
- [self main];
- [self.lock unlock];
- }
- - (void)main{
-
- @try {
- // 必须为自定义的 operation 提供 autorelease pool,因为 operation 完成后需要销毁。
- @autoreleasepool {
- // 提供一个变量标识,来表示需要执行的操作是否完成了,当然,没开始执行之前,为NO
- _taskIsFinished = NO;
-
- //只有当没有执行完成和没有被取消,才执行自定义的相应操作
- if (self.taskIsFinished == NO && [self isCancelled] == NO) {
- [self startExcuting];
- }
-
- }
- }@catch (NSException * e) {
- NSLog(@"Exception %@", e);
- }
- }
- - (BOOL)isExecuting{
- return _executing;
- }
- - (BOOL)isFinished{
- return _finished;
- }
- - (BOOL)isAsynchronous{
- return YES;
- }
- /**
- * 1.cancel方法调用后 该operation将会取消并从queue中移除
- * 2.若队列中有等待中的任务,将会自动执行
- */
- - (void)cancel{
-
- BOOL isWaiting = self.downloadModel.status == RQDownloadStatus_Waiting;
- [self.downloadTask cancel];
- [self removeObserver];
- [super cancel];
- //等待状态下取消时 无需将isFinished设置为已完成 等调用start方法时检测canceled来设置
- //参考:https://blog.csdn.net/loggerhuang/article/details/50015573
- if (!isWaiting) {
- [self completeOperation];
- }
- }
- @end
|