进入后台时的 NSURLConnection 最佳实践

Posted

技术标签:

【中文标题】进入后台时的 NSURLConnection 最佳实践【英文标题】:NSURLConnection best practise when enter background 【发布时间】:2013-08-03 15:50:31 【问题描述】:

我在我的应用程序中注意到,当应用程序在加载时进入后台时会导致错误,例如 "timeout" 或 "host name not found" 。

这是由于进程长时间不允许连接在后台运行。

但是这种错误信息会影响用户体验。那么我应该怎么做才能取消交易呢?我应该取消所有连接吗?我试图在这里搜索 SO 中的问答,但找不到答案。

有关更多信息,我的应用程序使用 NSURLConnectionDelegate 方法。我有一个存储单例来管理与我的服务器的所有连接。 NSURLConnection 也在自定义对象中被调用和管理。

我试图只是 [连接取消] 在 - applicationDidEnterBackground: 但这会使 UI 损坏,因为我加载数据以放入 UITableViewCell 等。谁能指出解决此类问题的示例?

更新代码:

- (void)applicationDidEnterBackground:(UIApplication *)application

 __block UIBackgroundTaskIdentifier backgroundTask; backgroundTask = 
[application      beginBackgroundTaskWithExpirationHandler: ^  
[application endBackgroundTask:backgroundTask]; 
backgroundTask = UIBackgroundTaskInvalid; ];  

  

我可以把这段代码放在 appDelegate 中吗?与将 beginBackgroundTaskWithExpirationHandler 放在我想在后台继续运行的任务之前和 endBackgroundTask 在该任务完成后放置相比,这样做的缺点是什么?我的代码有一个直接处理 NSURLConnection 的对象。

【问题讨论】:

你不是说[connection cancel]吗? 是的,没错。就是[连接取消]没有停止。 【参考方案1】:

您可以在进入后台后继续运行NSURLConnection 一段时间。苹果没有公布确切的时间段,但它是 10 分钟。有关如何使用beginBackgroundTaskWithExpirationHandler: 请求更多时间来完成下载的详细信息,请参阅Executing a Finite-Length Task in the Background。

在大多数情况下,您不应主动取消下载。您应该等到系统使您过期,然后在那时处理错误。如果您的下载时间很短,则没有理由取消它(在大多数情况下,连接最昂贵的事情就是首先设置它)。如果这是一个很长的下载,那么如果它不在后台继续,用户会很生气。 (这假设您正在下载东西,因为用户请求它。)

【讨论】:

感谢您的建议。我的应用程序将有另一个处理下载内容的对象。所以在 applicationDidEnterBackground 委托中。我可以只调用 __block UIBackgroundTaskIdentifier backgroundTask; backgroundTask = [应用程序 beginBackgroundTaskWithExpirationHandler: ^ [应用程序 endBackgroundTask: backgroundTask];背景任务 = UIBackgroundTask 无效; ]; ?当应用程序进入后台时,handlerBlock 将在 10 分钟后被调用。所以无论如何它都会 endBackgroundTaks。这个看起来更容易,但如果它浪费太多资源,我就不会了【参考方案2】:

首先,最好在应用程序委托上进行此调用,以便可以关闭 NavigationController 中的 View。其次,用beginBackgroundTaskWithExpirationHandler:标记后台处理的开始,用endBackgroundTask:结束它,如下所示:

.h:

UIBackgroundTaskIdentifier bgTask;

.m:

- (void)sendPhoto:(UIImage *)image

  UIApplication *app = [UIApplication sharedApplication];

  bgTask = [app beginBackgroundTaskWithExpirationHandler:^ 
    [app endBackgroundTask:bgTask]; 
    bgTask = UIBackgroundTaskInvalid;
  ];


  NSLog(@"Sending picture...");

  // Init async NSURLConnection

  // ....


- (void)connectionDidFinishLoading:(NSURLConnection *)connection 

  NSLog(@"Picture sent.");

  UIApplication *app = [UIApplication sharedApplication];

  if (bgTask != UIBackgroundTaskInvalid) 
    [app endBackgroundTask:bgTask]; 
    bgTask = UIBackgroundTaskInvalid;
  

还要记住一件很重要的事情:

You have 10 minutes before ios terminates your app. You can check this time with [app backgroundTimeRemaining]

【讨论】:

感谢您的回答。但我不是很清楚。你的意思是我应该把那种代码放在控制器或 appDelegate 中? 或者我可以把这段代码放在 appDelegate - (void)applicationDidEnterBackground:(UIApplication *)application __block UIBackgroundTaskIdentifier backgroundTask; backgroundTask = [应用程序 beginBackgroundTaskWithExpirationHandler: ^ [应用程序 endBackgroundTask: backgroundTask];背景任务 = UIBackgroundTask 无效; ]; 是的,您只需将此代码放入您的应用程序委托中就可以了! 那么您的代码和我在评论中发布的代码有什么区别?

以上是关于进入后台时的 NSURLConnection 最佳实践的主要内容,如果未能解决你的问题,请参考以下文章

应用程序进入后台时的popToRootViewController

有啥方法可以强制异步 NSURLConnection 在后台调用 connectionDidFinishLoading 吗?

进入后台时的Android缩略图

URLSession downloadTask 在后台运行时的行为?

应用程序在后台时的地理围栏设备

iOS:应用程序从后台返回后访问 EAOutputStream 时的 SIGPIPE