如何防止恢复重复的 NSURLSessionTask

Posted

技术标签:

【中文标题】如何防止恢复重复的 NSURLSessionTask【英文标题】:How to prevent resume the duplicate NSURLSessionTask 【发布时间】:2015-08-07 02:02:10 【问题描述】:

我正在尝试在我的项目中使用 NSURLSession。这就是这种情况,我想检查是否已经存在具有相同请求的任何任务。所以我用getTasksWithCompletionHandler检查,如果没有,则恢复块中的任务。但是当我这样做时,数组中总是没有其他任务。

我错过了什么吗?

这是我的代码:

#import "HttpRequestHelper.h"


@implementation HttpRequestHelper

+(NSURLSession *)sharedDefaultSession
static dispatch_once_t once;
static NSURLSession * defaultSession;
dispatch_once(&once, ^
    NSURLSessionConfiguration * configure = [NSURLSessionConfiguration defaultSessionConfiguration];
    configure.HTTPMaximumConnectionsPerHost = 5;
    configure.timeoutIntervalForRequest = kTimeOutInterval;
    configure.timeoutIntervalForResource = kTimeOutInterval;
    defaultSession = [NSURLSession sessionWithConfiguration:configure delegate:nil delegateQueue:[NSOperationQueue mainQueue]];
);

return defaultSession;


+(void)resultFromRequest:(NSURLRequest *)request withCompleter:(downloadCompleter)completer

NSURLSessionDataTask * dataTask = [[self sharedDefaultSession] dataTaskWithRequest:request completionHandler:^(NSData *data, NSURLResponse *response, NSError *error) 
//            NSLog(@"response %@",response);
//            NSLog(@"url:%@\nData:%@",request.URL,[[NSString alloc]initWithData:data encoding:NSUTF8StringEncoding]);
    if (!error) 
        NSHTTPURLResponse * httpResponse = (NSHTTPURLResponse *)response;
        if (httpResponse.statusCode == 200) 
            completer([self jsonObjectFromData:data]);
         else 
            completer(nil);
            // handle http error
        
     else 
        completer(nil);
        NSLog(@"network error %@",error.localizedDescription);
        // handle network error
    
];

NSLog(@"check %@",dataTask);
[self checkAndResumeTask:dataTask];


+(void)checkAndResumeTask:(NSURLSessionTask *)task
[[self sharedDefaultSession] getTasksWithCompletionHandler:^(NSArray *dataTasks, NSArray *uploadTasks, NSArray *downloadTasks) 
    BOOL isNewTask = (![self containTask:task inTaskArray:dataTasks]&&![self containTask:task inTaskArray:downloadTasks]);
    NSLog(@"isNewTask : %d %@",isNewTask,[self sharedDefaultSession] );
    if (isNewTask) 
        if (!IS_LOWER_ios8) 
            task.priority = NSURLSessionTaskPriorityHigh;
        
        NSLog(@"%@ resume",task);
        [task resume];
     else 
        [task cancel];
    
];


+(BOOL)containTask:(NSURLSessionTask *)task inTaskArray:(NSArray *)taskArray
NSString * taskIndepentId = [task.originalRequest valueForHTTPHeaderField:IndependentID]?:@"";
NSString * taskUrlString = task.originalRequest.URL.absoluteString;
for (NSURLSessionTask * oldTask in taskArray) 
    if (!IS_LOWER_IOS8) 
        oldTask.priority = NSURLSessionTaskPriorityDefault;
    

    NSString * oldTaskIndepentId = [oldTask.originalRequest valueForHTTPHeaderField:IndependentID]?:@"";
    NSString * oldTaskUrlString = oldTask.originalRequest.URL.absoluteString;
    if ([taskIndepentId isEqualToString:oldTaskIndepentId] && [taskUrlString isEqualToString:oldTaskUrlString] && oldTask!=task) 
        return YES;
    

return NO;


+(id)jsonObjectFromData:(NSData*)data

NSError * dataError;
NSDictionary * dic =   [NSJSONSerialization JSONObjectWithData:data options:kNilOptions error:&dataError];
if (dataError) 
    NSLog(@"json data error : %@",dataError);

return dic;


+(void)cancelTaskForRequest:(NSURLRequest *)request
[[self sharedDefaultSession] getTasksWithCompletionHandler:^(NSArray *dataTasks, NSArray *uploadTasks, NSArray *downloadTasks) 
    [self cancelRequest:request ForArrayTasks:dataTasks];
    [self cancelRequest:request ForArrayTasks:uploadTasks];
    [self cancelRequest:request ForArrayTasks:downloadTasks];
];


+(void)cancelRequest:(NSURLRequest *)request ForArrayTasks:(NSArray *)arrayTasks
for (NSURLSessionTask * task in arrayTasks) 
    NSLog(@"task %@",task);
    NSURLRequest * requestTask = task.originalRequest;
    BOOL isSameURL = [requestTask.URL.absoluteString isEqualToString:request.URL.absoluteString];
    BOOL isSameID = [[requestTask valueForHTTPHeaderField:IndependentID] isEqualToString:[request valueForHTTPHeaderField:IndependentID]];
    if (isSameURL && isSameID) 
        [task cancel];
    



@end

我尝试在检查checkAndResumeTask:之前添加[dataTask resume];,并且任务将在getTasksWithCompletionHandler的数组中,但是在我恢复之前如何检查?

这是正确的做法吗?

如有任何意见,我将不胜感激,谢谢。

------- 编辑 --------

@dgatwood:我确定其他任务正在运行

+(void)checkAndResumeTask:(NSURLSessionTask *)task
NSLog(@"\ncheck task %@ \nfor URL %@ \nin thread %@",task,task.originalRequest.URL.absoluteString,[NSOperationQueue currentQueue]);
[[self sharedDefaultSession] getTasksWithCompletionHandler:^(NSArray *dataTasks, NSArray *uploadTasks, NSArray *downloadTasks) 
    NSLog(@"\ncheck task %@ \nfor URL %@ \nin thread %@ \n\n old tasks: %@,\n%@",task,task.originalRequest.URL.absoluteString,[NSOperationQueue currentQueue],dataTasks,downloadTasks);
    BOOL isNewTask = (![self containTask:task inTaskArray:dataTasks]&&![self containTask:task inTaskArray:downloadTasks]);
    NSLog(@"isNewTask : %d %@",isNewTask,[self sharedDefaultSession] );
    if (isNewTask) 
        if (!IS_LOWER_IOS8) 
            task.priority = NSURLSessionTaskPriorityHigh;
        
        [task resume];
        NSLog(@"\nresume task :%@",task);
     else 
        [task cancel];
    
];

我在代码中、块之前、块之后和恢复任务之后打印信息。

我用相同的请求调用三个任务。

下面是我的结果

2015-09-04 01:59:45.216 XXX[26387:1632660] **resume task :<__NSCFLocalDataTask: 0x7fa428e81a40> taskIdentifier: 1 running **之后可以看到这个任务正在运行,但是这之后的block return数组中仍然没有任务。

这是因为块在第一个任务恢复之前已经获取了数组吗?还是我出了什么问题?

2015-09-04 01:59:45.130 XXX[26387:1632660] 
check task <__NSCFLocalDataTask: 0x7fa428e81a40> taskIdentifier: 1   suspended  
for URL https://XXX/AppApi/api/account/BonusDetails 
in thread <NSOperationQueue: 0x7fa428d05f70>name = 'NSOperationQueue Main Queue'
2015-09-04 01:59:45.131 XXX[26387:1632660] 
check task <__NSCFLocalDataTask: 0x7fa42b034e70> taskIdentifier: 2   suspended  
for URL https://XXX/AppApi/api/account/BonusDetails 
in thread <NSOperationQueue: 0x7fa428d05f70>name = 'NSOperationQueue Main Queue'
2015-09-04 01:59:45.132 XXX[26387:1632660] 
check task <__NSCFLocalDataTask: 0x7fa42b037180> taskIdentifier: 3   suspended  
for URL https://XXX/AppApi/api/account/BonusDetails 
in thread <NSOperationQueue: 0x7fa428d05f70>name = 'NSOperationQueue Main Queue'
2015-09-04 01:59:45.215 XXX[26387:1632660] 
check task <__NSCFLocalDataTask: 0x7fa428e81a40> taskIdentifier: 1   suspended  
for URL https://XXX/AppApi/api/account/BonusDetails 
in thread <NSOperationQueue: 0x7fa428d05f70>name = 'NSOperationQueue Main Queue' 

 old tasks: (
),
(
)
2015-09-04 01:59:45.216 XXX1632660] isNewTask : 1 <__NSURLSessionLocal: 0x7fa428e80d80>
2015-09-04 01:59:45.216 XXX[26387:1632660] 
**resume task :<__NSCFLocalDataTask: 0x7fa428e81a40> taskIdentifier: 1   running **
2015-09-04 01:59:45.255 XXX[26387:1632660] 
check task <__NSCFLocalDataTask: 0x7fa42b034e70> taskIdentifier: 2   suspended  
for URL XXX/AppApi/api/account/BonusDetails 
in thread <NSOperationQueue: 0x7fa428d05f70>name = 'NSOperationQueue Main Queue' 

 old tasks: (
),
(
)
2015-09-04 01:59:45.255 XXX[26387:1632660] isNewTask : 1 <__NSURLSessionLocal: 0x7fa428e80d80>
2015-09-04 01:59:45.255 XXX[26387:1632660] 
resume task :<__NSCFLocalDataTask: 0x7fa42b034e70> taskIdentifier: 2   running 
2015-09-04 01:59:45.256 XXX[26387:1632660] 
check task <__NSCFLocalDataTask: 0x7fa42b037180> taskIdentifier: 3   suspended  
for URL https://XXX/AppApi/api/account/BonusDetails 
in thread <NSOperationQueue: 0x7fa428d05f70>name = 'NSOperationQueue Main Queue' 

 old tasks: (
),
(
)

【问题讨论】:

【参考方案1】:

您确定其他任务仍在运行吗?任务完成后,它不会出现在对 getTasksWithCompletionHandler、IIRC 的调用中。如果您需要在之前的请求完成后检查它们是否存在,您需要将原始 URL 或 URL 请求存储在 NSSet 或类似文件中。

【讨论】:

我只是改进问题,我确定任务正在运行 那么这很可能是线程问题。请求当前任务集的代码是否总是在主线程上运行? 您的代码是否会在您将任务添加到会话和检查它之间产生运行循环?

以上是关于如何防止恢复重复的 NSURLSessionTask的主要内容,如果未能解决你的问题,请参考以下文章

防止重复数据

如何防止应用程序/游戏在一段时间后恢复时崩溃

Drupal 6如何防止特定用户的自动密码恢复和重置

如何防止 d3.js 强制布局在恢复/重启时脉动/弹跳

如何防止用户重复提交数据

单击右/左控件后如何防止Bootstrap轮播恢复幻灯片动画