[英]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];
我第一次單擊下載按鈕時效果很好。 第二次NSURLSession
只didCompleteWithError
方法。 這是我第二次從日志中得到的信息:
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.