iOS后台获取时间限制崩溃

时间:2014-08-08 19:23:48

标签: ios objective-c afnetworking background-process

我使用NSScreencast episode #92中的大纲设置了后台提取。

- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions 
{
  ...
  [application setMinimumBackgroundFetchInterval:UIApplicationBackgroundFetchIntervalMinimum];
  ...
}

- (void)application:(UIApplication *)application performFetchWithCompletionHandler:(void (^)(UIBackgroundFetchResult))completionHandler 
{
    if ([[MyAppAPIClient manager] reachable] && [[Session manager] userSignedIn])
    {
        [[[Session manager] user] fetchBackgroundWithSuccess:^(NSURLSessionDataTask *task, NSDictionary *responseObject) {
            completionHandler(UIBackgroundFetchResultNewData);
        } failure:^(NSURLSessionDataTask *task, NSError *error) {
            completionHandler(UIBackgroundFetchResultFailed);
        }];
    }
    else
        completionHandler(UIBackgroundFetchResultNoData);
}

对于fetchBackgroundWithSuccess:failure方法,我正在使用AFNetworking NSURLSessionDataTask

不幸的是,有时我会收到错误

MyApp[3344] has active assertions beyond permitted time:
{(
    <BKProcessAssertion: 0x178279c00> identifier: Background Content Fetching (1596) process: MyApp[3344] permittedBackgroundDuration: 30.000000 reason: backgroundContentFetching owner pid:16 preventSuspend      preventThrottleDownUI  preventIdleSleep  preventSuspendOnSleep 
)}

它完全崩溃我的应用程序,注销我的用户,并且所有用户的数据都被清除干净。

不确定如何解决这个问题,但任何方向都会受到高度赞赏。

更新:

我存储数据的方式是使用NSKeyedArchiver的archiveRootObject:toFileunarchiveObjectWithFile。它会跟踪用户是否已登录,其user_id和其他重要信息。这都是使用类User的单个对象在内存中缓存的。

期间调用

archiveRootObject:toFile

  1. applicationWillResignActive
  2. applicationDidEnterBackground
  3. applicationWillTerminate
  4. applicationDidReceiveMemoryWarning
  5. 如果单例为零,则根据需要调用

    unarchiveObjectWithFile

    注销并擦除干净意味着单例为零,unarchiveObjectWithFile无法检索任何数据。

2 个答案:

答案 0 :(得分:2)

每个后台提取都有一个时间限制,我相信30秒。与可以利用到期处理程序自动清理并执行您在时间不足时需要执行的操作的正常后台任务不同,后台提取当前不具备该功能。你可以处理这个问题的一种方法是启动一个NSTimer,计划在你的后台获取中运行20-25秒,并让该计时器调用一个函数来处理你是否还没有完成你的后台任务来阻止你我正在做并清理所有内容,以便应用程序可以恢复正常的后台运行。来自文档:

  

调用此方法时,您的应用最长可达30秒   挂钟时间执行下载操作并调用   指定的完成处理程序块在实践中,您的应用应该致电   下载后尽快完成处理程序块   需要的数据。 如果你没有及时调用完成处理程序,那么你的   应用已终止。

https://developer.apple.com/library/ios/documentation/uikit/reference/uiapplicationdelegate_protocol/Reference/Reference.html#//apple_ref/occ/intfm/UIApplicationDelegate/application:performFetchWithCompletionHandler

大胆的部分听起来就像正在发生的事情。无论哪种方式,你需要在30秒内完成你想要完成的任务,否则你需要取消它或你的应用程序将被终止。

答案 1 :(得分:1)

这个问题有点陈旧但是下面的代码可能会帮助一些对基于Mike答案的代码片段感兴趣的开发人员。正如迈克建议我在使用太多时间时也使用NSTimer来取消后台获取。在我的后台获取中,我使用AFNetworking访问我的web服务以获取一些新数据。如果时间(我选择了25秒)结束,我只需取消我在NSOperationQueue中对Web服务的所有打开请求。这是我的代码:

func application(application: UIApplication, performFetchWithCompletionHandler completionHandler: (UIBackgroundFetchResult) -> Void) {

    // This Handler accesses my webservice using AFNetworking and creates NSOperationQueue to enqueue the requests
    backgroundSearchHandler = BackgroundFetchSearchHandler()

    let cancelTimer = NSTimer.scheduledTimerWithTimeInterval(25, target: self, selector: "cancelBackgroundFetch", userInfo: nil, repeats: false)

    backgroundSearchHandler!.findNewResultsForSavedSearches { (completed, newResultCount) -> Void in

        // This block is also called when the timer is over and the requests were cancelled

        // Stop the timer
        cancelTimer.invalidate()

        if !completed
        {
            completionHandler(.Failed)
            return
        }

        if newResultCount <= 0
        {
            completionHandler(.NoData)
            return
        }

        completionHandler(.NewData)
    }
}

func cancelBackgroundFetch()
{
    backgroundSearchHandler?.cancel()
    // This is calling cancelAllOperations() on the NSOperationQueue in my background search handler
}

希望这有帮助!