簡體   English   中英

iOS - NSURLSession 第二次不工作

[英]iOS - NSURLSession not working the second time

我有一個包含進度條的UIView 我想要做的很簡單,我有一個按鈕,用戶單擊該按鈕,應用程序下載文件並在進度條中顯示進度。 當用戶第一次單擊下載按鈕時,我可以執行此操作。 但是當用戶第二次點擊再次下載時,不會調用NSURLSession委托。

我的 UIView .m

- (id)initWithFrame:(CGRect)frame
{
    self = [super initWithFrame:frame];
    if (self) {
        [self configure];
    }
    return self;
}

- (id)initWithCoder:(NSCoder *)aDecoder
{
    self = [super initWithCoder:aDecoder];
    if (self) {
        [self configure];
    }
    return self;
}

-(void)configure
{
    [self createSpinner];
    [self createProgressBar];

    NSArray *URLs = [[NSFileManager defaultManager] URLsForDirectory:NSDocumentDirectory inDomains:NSUserDomainMask];
    self.docDirectoryURL = [URLs objectAtIndex:0];

    NSURLSessionConfiguration *sessionConfiguration = [NSURLSessionConfiguration backgroundSessionConfigurationWithIdentifier:@"com.tinkytickles"];
    sessionConfiguration.HTTPMaximumConnectionsPerHost = 1;
    self.session = [NSURLSession sessionWithConfiguration:sessionConfiguration
                                                 delegate:self
                                            delegateQueue:nil];
}

-(void)createSpinner
{
    [self setBackgroundColor:[UIColor colorWithWhite:1.0f alpha:0.5f]];

    spinner = [[UIActivityIndicatorView alloc] initWithActivityIndicatorStyle:UIActivityIndicatorViewStyleGray];
    [self addSubview:spinner];
    [spinner setColor:original_new_dark_grey];
    [spinner setUserInteractionEnabled:NO];
    [spinner setCenter:CGPointMake([[UIScreen mainScreen] bounds].size.width/2, [[UIScreen mainScreen] bounds].size.height/2)];
    [spinner setFrame:CGRectMake(0, 0, [UIScreen mainScreen].bounds.size.width, [UIScreen mainScreen].bounds.size.height)];
    [spinner startAnimating];
}

-(void)createProgressBar
{
    self.progressBar = [[TYMProgressBarView alloc] initWithFrame:CGRectMake(0, 0, 280, 15)];
    [self.progressBar setBarBackgroundColor:[UIColor whiteColor]];
    [self.progressBar setBarBorderColor:original_new_dark_grey];
    [self.progressBar setBarFillColor:original_new_dark_grey];
    [self.progressBar setBarBorderWidth:1.0f];
    [self addSubview:self.progressBar];
    [self.progressBar setCenter:CGPointMake([[UIScreen mainScreen] bounds].size.width/2, [[UIScreen mainScreen] bounds].size.height/2)];
    [self.progressBar setHidden:YES];

    self.label = [[UILabel alloc] initWithFrame:CGRectMake(self.progressBar.frame.origin.x, self.progressBar.frame.origin.y - 30, self.progressBar.frame.size.width, 25)];
    [self.label setText:NSLocalizedString(locDownloading, nil)];
    [self.label setTextAlignment:NSTextAlignmentCenter];
    [self.label setTextColor:original_new_dark_grey];
    [self.label setFont:quicksand_14];
    [self addSubview:self.label];
    [self.label setHidden:YES];
}

-(void)showProgressBarWithProgress:(CGFloat)progress withText:(NSString *)text
{
    [spinner setHidden:YES];

    [self.label setText:[NSString stringWithFormat:NSLocalizedString(locDownloadingAt, nil), text]];
    [self.label setHidden:NO];
    [self.progressBar setHidden:NO];
    [self.progressBar setProgress:progress];
}


-(void)stopAnimating
{
    [spinner stopAnimating];
}

-(void)startDownloadingURL:(PromoterDownloadInfo *)downloadInfo
{
    info = downloadInfo;

    if (!info.isDownloading)
    {
        if (info.taskIdentifier == -1)
        {
            info.downloadTask = [self.session downloadTaskWithURL:[NSURL URLWithString:info.downloadSource]];
            info.taskIdentifier = info.downloadTask.taskIdentifier;
            [info.downloadTask resume];
        }
        else
        {
            info.downloadTask = [self.session downloadTaskWithResumeData:info.taskResumeData];
            [info.downloadTask resume];
            info.taskIdentifier = info.downloadTask.taskIdentifier;
        }
    }
    else
    {
        [info.downloadTask cancelByProducingResumeData:^(NSData *resumeData) {
            if (resumeData != nil) {
                info.taskResumeData = [[NSData alloc] initWithData:resumeData];
            }
        }];
    }

    info.isDownloading = !info.isDownloading;
}

-(void)stopDownload:(PromoterDownloadInfo *)downloadInfo
{
    if (!info.isDownloading)
    {
        if (info.taskIdentifier == -1)
        {
            info.downloadTask = [self.session downloadTaskWithURL:[NSURL URLWithString:info.downloadSource]];
        }
        else
        {
            info.downloadTask = [self.session downloadTaskWithResumeData:info.taskResumeData];
        }

        info.taskIdentifier = info.downloadTask.taskIdentifier;
        [info.downloadTask resume];
        info.isDownloading = YES;
    }

    [self stopAnimating];
    [self removeFromSuperview];
}

#pragma mark - NSURLSession Delegate method implementation

-(void)URLSession:(NSURLSession *)session downloadTask:(NSURLSessionDownloadTask *)downloadTask didFinishDownloadingToURL:(NSURL *)location
{
    NSError *error;
    NSFileManager *fileManager = [NSFileManager defaultManager];

    NSString *destinationFilename = downloadTask.originalRequest.URL.lastPathComponent;
    NSURL *destinationURL = [self.docDirectoryURL URLByAppendingPathComponent:destinationFilename];

    if ([fileManager fileExistsAtPath:[destinationURL path]]) {
        [fileManager removeItemAtURL:destinationURL error:nil];
    }

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

    if (success) {
        [[NSOperationQueue mainQueue] addOperationWithBlock:^{
            [self stopAnimating];
            [self removeFromSuperview];
        }];
    }
    else
    {
        NSLog(@"Unable to copy temp file. Error: %@", [error localizedDescription]);
    }
}


-(void)URLSession:(NSURLSession *)session task:(NSURLSessionTask *)task didCompleteWithError:(NSError *)error{
    if (error != nil) {
        NSLog(@"Download completed with error: %@", [error localizedDescription]);
    }
    else{
        NSLog(@"Download finished successfully.");
    }
}


-(void)URLSession:(NSURLSession *)session downloadTask:(NSURLSessionDownloadTask *)downloadTask didWriteData:(int64_t)bytesWritten totalBytesWritten:(int64_t)totalBytesWritten totalBytesExpectedToWrite:(int64_t)totalBytesExpectedToWrite
{
    if (totalBytesExpectedToWrite == NSURLSessionTransferSizeUnknown) {
        NSLog(@"Unknown transfer size");
    }
    else
    {
        dispatch_async(dispatch_get_main_queue(), ^{
            info.downloadProgress = (double)totalBytesWritten / (double)totalBytesExpectedToWrite;
            [self showProgressBarWithProgress:info.downloadProgress withText:info.fileTitle];
        });
    }
}


-(void)URLSessionDidFinishEventsForBackgroundURLSession:(NSURLSession *)session
{
    AppDelegate *appDelegate = (AppDelegate *)[UIApplication sharedApplication].delegate;

    // Check if all download tasks have been finished.
    [self.session getTasksWithCompletionHandler:^(NSArray *dataTasks, NSArray *uploadTasks, NSArray *downloadTasks) {

        if ([downloadTasks count] == 0) {
            if (appDelegate.backgroundTransferCompletionHandler != nil) {
                // Copy locally the completion handler.
                void(^completionHandler)() = appDelegate.backgroundTransferCompletionHandler;

                // Make nil the backgroundTransferCompletionHandler.
                appDelegate.backgroundTransferCompletionHandler = nil;

                [[NSOperationQueue mainQueue] addOperationWithBlock:^{
                    // Call the completion handler to tell the system that there are no other background transfers.
                    completionHandler();

                    // Show a local notification when all downloads are over.
                    UILocalNotification *localNotification = [[UILocalNotification alloc] init];
                    localNotification.alertBody = NSLocalizedString(locDownloadComplete, nil);
                    [[UIApplication sharedApplication] presentLocalNotificationNow:localNotification];
                }];
            }
        }
    }];
}

我像這樣使用這個 UIView:

PromoterDownloadInfo *info = [[PromoterDownloadInfo alloc] initWithFileTitle:self.title andDownloadSource:@"https://www.mywebsite.com/file.zip"];
PromotersDownloadView *downloadView = [[PromotersDownloadView alloc] initWithFrame:[[UIScreen mainScreen] bounds]];
[self.navigationController.view addSubview:downloadView];
[downloadView startDownloadingURL:info];

我第一次單擊下載按鈕時效果很好。 第二次NSURLSessiondidCompleteWithError方法。 這是我第二次從日志中得到的信息:

2016-05-12 00:50:47.440 APP[32990:1230071] A background URLSession with identifier com.app already exists!
2016-05-12 00:50:50.614 APP[32990:1230386] Download finished successfully.

我究竟做錯了什么? 我試圖只創建一次NSURLSessionConfiguration但這樣就沒有調用委托方法。 我該怎么辦?

你說:

我第一次單擊下載按鈕時效果很好。 ...這是我第二次從日志中得到的信息:

 2016-05-12 00:50:47.440 APP[32990:1230071] A background URLSession with identifier com.app already exists!<br />

該錯誤指出您只想為給定的標識符實例化一個后台NSURLSession (並且您通常只需要/想要一個后台會話)。 如果您要實例化多個會話,您會為它們提供唯一標識符,但處理后台會話已經足夠復雜,而不必擁有多個會話。 我建議您只需要一個后台會話。

你說:

我試圖只創建一次NSURLSessionConfiguration但這樣就沒有調用委托方法。

是的,您應該有一個會話配置。 而且,同樣重要的是,只有一個后台會話對象。

我懷疑您的委托對象存在問題,無法跟蹤它應該更新哪個視圖。 或者,您可能丟失了對會話對象的引用,而您的引用為nil 這可能是幾個不同的事情,如果不看看你是如何做到的,就很難知道。

我建議將此會話配置代碼移出視圖,並擁有一些您可以在任何地方引用的共享實例(例如,單例運​​行良好,因此您可以從第一次需要的任何地方實例化它,無論是從視圖還是從應用程序委托的handleEventsForBackgroundURLSession方法)。

唯一的挑戰是如何跟蹤哪些視圖跟蹤哪些網絡請求。 您是否希望有一個單獨的視圖來跟蹤所有未完成的請求,而不管該視圖何時被實例化? 如果是這樣,您可以使用NSNotificationCenter通知(這樣,任何想要收到進度更新通知的視圖都可以只觀察您的自定義通知)。 還是給定的視圖只關心您從該視圖發起的請求?在這種情況下,您可能會維護將taskIdentifier值映射到需要了解狀態更新的視圖或對象的字典(您可以通過什么方式讓會話對象保持跟蹤哪些視圖關心哪些任務)。 這僅取決於您的應用程序的要求。

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM