gpt4 book ai didi

iOS - NSURLSession 第二次不工作

转载 作者:行者123 更新时间:2023-11-29 12:00:39 25 4
gpt4 key购买 nike

我有一个包含进度条的 UIView。我想做的很简单,我有一个按钮,用户单击该按钮,应用程序下载文件并在进度条中显示进度。当用户第一次单击下载按钮时,我能够执行此操作。但是当用户第二次点击再次下载时,NSURLSession delegates 并没有被调用。

我的 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 但这样就不会调用任何委托(delegate)方法。我该怎么办?

最佳答案

你说:

The first time I clicked the download button it works great. ... Here is what I get from log the second time:

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

该错误指出您只想为给定标识符实例化一个后台NSURLSession(并且您通常只需要/想要一个后台 session )。如果你要实例化多个,你会给他们唯一的标识符,但是处理后台 session 已经足够复杂了,没有不必要的多个 session 。我建议您只需要一个后台 session 。

你说:

I tried to create NSURLSessionConfiguration only once but this way no delegate method gets called.

是的,你应该有一个 session 配置。而且,同样重要的是,只有一个后台 session 对象。

我怀疑您的委托(delegate)对象存在问题,无法跟踪它应该更新哪个 View 。或者,您可能丢失了对 session 对象的引用,而您的引用是 nil。这可能是几件不同的事情,如果不看看您是如何做到的,就很难知道。

我建议将此 session 配置代码移出 View ,并拥有一些可以在任何地方引用的共享实例(例如,单例运​​行良好,因此您可以从首先需要它的任何地方实例化它,无论是从 View 还是来自应用委托(delegate)的 handleEventsForBackgroundURLSession 方法)。

唯一的挑战是如何跟踪哪些 View 正在跟踪哪些网络请求。您是否希望有一个单一的 View 来跟踪所有未完成的请求,而不管该 View 何时被实例化?如果是这样,您可以使用 NSNotificationCenter 通知(这样,任何想要收到进度更新通知的 View 都可以只观察您的自定义通知)。或者给定的 View 是否只关心您从该 View 发起的请求?在这种情况下,您可以维护将 taskIdentifier 值映射到 View 或对象需要了解状态更新的字典(什么方式你可以让你的 session 对象跟踪哪些 View 关心哪些任务)。这仅取决于您应用的要求。

关于iOS - NSURLSession 第二次不工作,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/37170167/

25 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com