内部nsurlsession和后台传输

时间:2018-08-19 20:24:43

标签: nsurlsession nsurlsessiondownloadtask

我在项目中工作,需要在后台下载大型JSON响应(即使应用程序崩溃)。

我还想了解后台传输服务的内部原理,即iOS在内部进行的操作以及操作方式。因此,我已将调用堆栈保存在UserDefaults中。

有2种情况。

1)没有应用崩溃。

(
Download task created with taskidentifier : 1,
didfinishdownloadingtourl,
File downloaded for taskidentifier : 1,
didCompleteWithError->success for taskIdentifier 1,
)

2)当我在下载开始后使用空指针取消引用手动使应用程序崩溃时,结果很奇怪。

(
Download task created with taskidentifier : 1,
Download task created with taskidentifier : 2,
handleEventsForBackgroundURLSession,
didfinishdownloadingtourl,
File downloaded for taskidentifier : 1,
didCompleteWithError->success for taskIdentifier 1,
URLSessionDidFinishEventsForBackgroundURLSession,
didfinishdownloadingtourl,
File downloaded for taskidentifier : 2,
didCompleteWithError->success for taskIdentifier 2
)

我只创建了1个任务。 iOS是否会自动创建第二个任务? 这里发生了什么。

这是我的代码

#import "KiviSyncLogin.h"
#import "AppDelegate.h"

@implementation KiviSyncLogin

-(instancetype)init{
    self = [super init];
    return self;
}

-(void)startSync{
    if(self.session != nil){
        NSLog(@"session is not nil");
        return;
    }
    self.session = [self setUpSession]; // init session with once token
    NSLog(@"This is session : %@",self.session);
    [self pullDataFromServerWithSession:self.session];
}

-(NSURLSession *)setUpSession{
    static dispatch_once_t onceToken;
    static NSURLSession *session = nil;

    dispatch_once(&onceToken, ^{
        NSURLSessionConfiguration *config = [NSURLSessionConfiguration backgroundSessionConfigurationWithIdentifier:@"com.kivi.login.sync"];
        config.HTTPMaximumConnectionsPerHost = 1;
        session = [NSURLSession sessionWithConfiguration:config delegate:self delegateQueue:nil];
    });
    return session;
}

-(void)pullDataFromServerWithSession:(NSURLSession *)session{

    NSString *serverUrlString = @"apiurl";
    NSMutableDictionary *mydic = [[NSMutableDictionary alloc] init];
    [mydic setObject:@"email" forKey:@"email"];
    [mydic setObject:@"password" forKey:@"password"];

    NSMutableURLRequest *request = [NSMutableURLRequest requestWithURL:[NSURL URLWithString:serverUrlString]];
    request.HTTPMethod = @"POST";
    request.HTTPBody = [[self encodedString:mydic] dataUsingEncoding:NSUTF8StringEncoding];

    self.downloadTask = [self.session downloadTaskWithRequest:request];
    NSLog(@"Download task %@ created with identifier : %ld", self.downloadTask,self.downloadTask.taskIdentifier);

    [self.downloadTask resume];
}


/*  common delegate Start  */

- (void)URLSession:(NSURLSession *)session downloadTask:(NSURLSessionDownloadTask *)downloadTask didFinishDownloadingToURL:(NSURL *)location{

    NSLog(@"file downloaded");
    NSFileManager *fileManager = [NSFileManager defaultManager];

    NSArray *URLs = [fileManager URLsForDirectory:NSDocumentDirectory inDomains:NSUserDomainMask];
    NSURL *documentsDirectory = [URLs objectAtIndex:0];

    NSURL *originalURL = [[downloadTask originalRequest] URL];
    NSURL *destinationURL = [documentsDirectory URLByAppendingPathComponent:[originalURL lastPathComponent]];
    NSError *errorCopy;

    [fileManager removeItemAtURL:destinationURL error:NULL];
    BOOL success = [fileManager copyItemAtURL:location toURL:destinationURL error:&errorCopy];

    if (success){

        /* Store data in database */

    }
    else{
        NSLog(@"Error during the copy: %@", [errorCopy localizedDescription]);
    }


}

- (void)URLSession:(NSURLSession *)session task:(NSURLSessionTask *)task didCompleteWithError:(NSError *)error {

    if(task.taskIdentifier == self.downloadTask.taskIdentifier){
        if(error){
            NSLog(@"Download task completed with error : %@",error);

            /* Show error that data is not downloaded */

            [self windUpSession];

        }else{

            NSLog(@"Download task completed successfully");

        }

    }
}

-(void)URLSessionDidFinishEventsForBackgroundURLSession:(NSURLSession *)session
{

    NSLog(@"Session %@ URL Session Did Finish Events For Background URL Session\n", session);
    dispatch_async(dispatch_get_main_queue(), ^{
        AppDelegate *appDelegate = (AppDelegate *)[UIApplication sharedApplication].delegate;
        [session getTasksWithCompletionHandler:^(NSArray *dataTasks, NSArray *uploadTasks, NSArray *downloadTasks) {
            if ([downloadTasks count] == 0 && [uploadTasks count] == 0) {
                if (appDelegate.backgroundTransferCompletionHandler != nil) {
                    NSLog(@"I have completion handler");
                    void(^completionHandler)(void) = appDelegate.backgroundTransferCompletionHandler;
                    appDelegate.backgroundTransferCompletionHandler = nil;
                    [self windUpSession];
                    [[NSOperationQueue mainQueue] addOperationWithBlock:^{
                        completionHandler();
                    }];
                }
            }
        }];
    });
}

- (void)windUpSession{
    //[self.session invalidateAndCancel];
    self.session = nil;
    self.downloadTask = nil;
}

- (void)URLSession:(NSURLSession *)session didBecomeInvalidWithError:(NSError *)error{

    if(error == nil){
        NSLog(@"Session is invalidated successfully");
    }else{
        NSLog(@"Error while invalidating session");
    }

}

/* Common Delegates End */


/* Helper methods */

-(NSString *)encodedString:(NSDictionary *)mydic{
    NSString *params = @"";//[NSString stringWithFormat:@"email=%@&password=%@",email,token];
    int i = 0;
    for (NSString *key in [mydic allKeys]) {
        NSString *stringToBeAppended;
        if(i++ == 0)
            stringToBeAppended = [NSString stringWithFormat:@"%@=%@",key,mydic[key]];
        else
            stringToBeAppended = [NSString stringWithFormat:@"&%@=%@",key,mydic[key]];
        params = [params stringByAppendingString:stringToBeAppended];
    }
    return params;
}

@end

如果我在创建下载任务时添加了[self.session finishTasksAndInvalidate];,它将创建第二个任务,但由于会话无效而无法执行。

在这种情况下,调用堆栈为(如果我手动操纵应用程序。)

(
Download task created with taskidentifier : 1,
Download task created with taskidentifier : 2,
handleEventsForBackgroundURLSession,
didfinishdownloadingtourl,
File downloaded for taskidentifier : 1,
didCompleteWithError->success for taskIdentifier 1,
URLSessionDidFinishEventsForBackgroundURLSession
)

编辑1

我还注意到,当我的应用程序崩溃时,我正在崩溃的应用程序以及从其初始化类并开始获取的viewController的viewDidLoad方法被调用。

0 个答案:

没有答案