NSURLSession中所有NSURLSessionTasks的平均进度

Eri*_*ric 18 macos cocoa cocoa-touch ios nsurlsession

一个NSURLSession允许你添加大量NSURLSessionTask的后台下载.

如果你想检查一个单一的进度NSURLSessionTask,它就像这样简单

double taskProgress = (double)task.countOfBytesReceived / (double)task.countOfBytesExpectedToReceive;

但是,检查所有平均进度NSURLSessionTasksNSURLSession的最佳方法是什么?

我以为我会尝试平均所有任务的进度:

[[self backgroundSession] getTasksWithCompletionHandler:^(NSArray *dataTasks, NSArray *uploadTasks, NSArray *allDownloadTasks) {

    double totalProgress = 0.0;

    for (NSURLSessionDownloadTask *task in allDownloadTasks) {

        double taskProgress = (double)task.countOfBytesReceived / (double)task.countOfBytesExpectedToReceive;

        if (task.countOfBytesExpectedToReceive > 0) {
            totalProgress = totalProgress + taskProgress;
        }
        NSLog(@"task %d: %.0f/%.0f - %.2f%%", task.taskIdentifier, (double)task.countOfBytesReceived, (double)task.countOfBytesExpectedToReceive, taskProgress*100);
    }

    double averageProgress = totalProgress / (double)allDownloadTasks.count;

    NSLog(@"total progress: %.2f, average progress: %f", totalProgress, averageProgress);
    NSLog(@" ");

}];
Run Code Online (Sandbox Code Playgroud)

但这里的逻辑是错误的:假设你有50个期望下载1MB的任务和3个期望下载100MB的任务.如果在3个大任务之前完成50个小任务,averageProgress将远高于实际平均进度.

所以你必须根据TOTAL countOfBytesReceived除以TOTAL 来计算平均进度countOfBytesExpectedToReceive.但问题是,只有在启动时才NSURLSessionTask会计算出这些值,并且在另一个任务完成之前可能无法启动.

那么,你如何检查的所有平均进度NSURLSessionTasks的NSURLSession?

Wil*_*ley 15

没错.我记得在1994年我写过OmniWeb的时候处理过这个问题.我们尝试了许多解决方案,包括只是让进度条旋转而不是显示进度(不受欢迎),或者随着新任务计算出它们将被添加到队列中的大小而增长(让用户感到不安因为他们看到了有时反向进展).

最后,大多数程序决定使用(包括iOS 5中的消息和Safari)是一种欺骗:例如,在消息中,他们知道发送消息的平均时间大约是1.5秒(仅限示例数字),因此,他们将进度条设置为动画,大约1.5秒完成,如果消息尚未实际发送,则只会延迟1.4秒.

现代浏览器(如Safari)通过将任务划分为多个部分并显示每个部分的进度条来改变这种方法.就像(仅限示例)一样,Safari可能会认为在DNS中查找URL通常需要0.2秒,因此他们会在0.2秒内为进度条的前1/10(或其他)设置动画,但当然他们会如果DNS查找分别更短或更长,则向前跳过(或等待1/10标记).

在你的情况下,我不知道你的任务是多么可预测,但应该有类似的欺骗.比如,大多数文件的平均大小是多少?如果是这样,你应该能够弄清楚50需要多长时间.或者,您只需将进度条划分为50个段,并在每次文件完成时填写一个段,并根据您获得的当前字节数/秒或根据您获得的文件数/秒来设置动画.您喜欢的远程或任何其他指标.

一个技巧就是使用Zeno的悖论,如果你必须开始或停止进度条 - 不要只是停下来或跳到下一个标记,而只是减速(并继续减速)或加速(并保持加速)直到你已经到了酒吧需要的地方.

祝好运!


HAS*_*HAS 13

在开始下载文件之前,您可以发送微小HEAD requests的文件来获取文件大小.您只需添加他们expectedContentLength的最终下载大小即可.

- (void)sizeTaskForURL:(NSURL *)url
{

    NSMutableURLRequest *request = [[NSMutableURLRequest alloc] initWithURL:url];
    [request setHTTPMethod:@"HEAD"];

    NSURLSessionDataTask *sizeTask =
    [[[self class] dataSession]
     dataTaskWithRequest:request
     completionHandler: ^(NSData *data, NSURLResponse *response, NSError *error)
     {
         if (error == nil)
         {
             NSHTTPURLResponse *httpResponse = (NSHTTPURLResponse *)response;

             if ([httpResponse statusCode] == 200) {

                 totalBytesExpectedToReceive += (double)[httpResponse expectedContentLength];

                 numberOfFileSizesReceived++;

                 NSLog(@"%lu/%lu files found. file size: %f", (unsigned long)numberOfFileSizesReceived, (unsigned long)numberOfTasks, totalBytesExpectedToReceive);

                 if (numberOfFileSizesReceived == numberOfTasks){
                     NSLog(@"%lu/%lu files found. total file size: %f", (unsigned long)numberOfFileSizesReceived, (unsigned long)numberOfTasks, totalBytesExpectedToReceive);
                 }
             }
             else {
                 NSLog(@"Bad status code (%ld) for size task at URL: %@", (long)[httpResponse statusCode], [[response URL] absoluteString]);
             }
         }
         else
         {
             NSLog(@"Size task finished with error: %@", error.localizedDescription);
         }
     }];

    [sizeTask resume];

}
Run Code Online (Sandbox Code Playgroud)

之后下载文件

这是它的样子:

在左侧,您可以看到它发送时HEAD requests尚未启动UIProgressView.完成后,下载文件.

应用

因此,如果您下载大文件,那么"浪费"那些制作HEAD请求的秒数可能会有用,从而向用户显示正确的进度而不是一些错误的进度.

在下载过程中,您(当然)希望使用委托方法来获取新数据的较小细分(否则进度视图将"跳转").