【问题标题】:Warning: Application delegate received call to -application:performFetchWithCompletionHandler: but the completion handler was never called警告:应用程序委托收到了对 -application:performFetchWithCompletionHandler 的调用:但从未调用过完成处理程序
【发布时间】:2017-07-22 09:33:29
【问题描述】:

我实现了performFetchWithCompletionHandler(即:后台获取)从服务器下载一些数据。

为此,在performFetchWithCompletionHandler 内部,为了不阻塞主线程,我创建并启动了一个新任务(所以在后台线程中),当任务完成时,我调用CompletionHandlerperformFetchWithCompletionHandler 一起给出

但无论如何,只要调用performFetchWithCompletionHandler,我就会在日志中收到立即(在下载任务完成之前)

警告:应用程序委托收到调用 -application:performFetchWithCompletionHandler: 但从未调用过完成处理程序。

所以我不明白该怎么做?这是否意味着所有工作都必须在过程performFetchWithCompletionHandler (所以在主线程中)performFetchWithCompletionHandler 必须在所有工作完成后才返回,通过这种方式阻塞主线程线程?

请帮助我了解如何正确操作

这是我的代码(它在 delphi 中,但它与我之前描述的完全一样,没有我用 sleep(15000) 替换的网络连接):

class procedure TMainForm.performFetchWithCompletionHandler(self: id; _cmd: SEL; application: PUIApplication; completionHandler: id);
begin

  aTaskID := SharedApplication.beginBackgroundTaskWithExpirationHandler(SynchLocationsTaskExpired);

  //load the Profiles in other thread to not block the calling thread
  fSynchLocationsTask := TThread.CreateAnonymousThread(
    procedure
    begin

      Sleep(15000);


      TThread.Queue(nil,
        procedure
        begin
          ExecutePerformFetchCompletionHandler(completionHandler, UIBackgroundFetchResultNewData); // ==> here the app crash like completionHandler is not valid anymore 
          SharedApplication.endBackgroundTask(aTaskID);  
        end);

    end);
  fSynchLocationsTask.FreeOnTerminate := False;
  fSynchLocationsTask.Start;

  // => here i receive in log Warning: Application delegate received call to -application:performFetchWithCompletionHandler: but the completion handler was never called

end;

【问题讨论】:

  • 你能给我们看看代码吗?
  • 有点难给大家看,因为它是用delphi写的,恐怕你看不懂,但过程和我说的一模一样。 performFetchWithCompletionHandler => 创建一个后台任务 => 当任务完成调用completionHandler
  • 你不必担心阻塞performFetchWithCompletionHandler中的主线程,因为你不在前台;但是,如果您在没有调用 completionHandler 的情况下从该函数返回,则必须在返回之前调用 beginBackgroundTaskWithExpirationHandler,并在调用完成处理程序后调用 endBackgroundTask
  • @Paulw11 :谢谢,您能否再澄清一点(我认为您甚至可以将其作为有用的答案)。在 performFetchWithCompletionHandler 内部的主线程中做所有事情是正常的做法(即:要走的路)吗?我知道应用程序不在前台,但如果任务需要 30 秒才能完成,而在中间用户想要将应用程序置于前台怎么办?另一方面,在 performFetchWithCompletionHandler 中使用 beginBackgroundTaskWithExpirationHandler 看起来更像是一个 hack
  • 问题是你在没有调用完成处理程序的情况下从函数返回并且iOS不知道你还有一个后台任务在运行;这就是为什么你需要打电话给beginBackgroundTaskWithExpirationHandler

标签: ios delphi background-fetch


【解决方案1】:
- (void)application:(UIApplication*)application performFetchWithCompletionHandler:(nonnull void (^)(UIBackgroundFetchResult))completionHandler {
    NSLog(@"Started background fetch");

    // Create URL session based on NSURLSession object which is required for all background fetch operations.
    MyUrlSession* urlSession = MyUrlSession.new;

    // Update is asynchronous so we have to tell to application that
    // we will call completion handler asynchronously. It prevents showing warning
    // that completion handler was never called.
    UIBackgroundTaskIdentifier backgroundTaskIdentifier = [application beginBackgroundTaskWithName:@"MyBgTask" expirationHandler:^{
        // This will cancel update and will call completion handler with error.
        NSLog(@"Expiration handler for background fetch");
        [urlSession cancel];
    }];

    // Start URL session with completion handler.
    [urlSession startWithCompletionHandler:^(BOOL successful, NSString *errorString) {
        NSLog(@"Background fetch completed with success = %d, error = %@", successful, errorString);
        [application endBackgroundTask:backgroundTaskIdentifier];
        completionHandler(successful ? UIBackgroundFetchResultNewData : UIBackgroundFetchResultFailed);
        NSLog(@"Background fetch has ended.");
    }];
}

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2021-03-18
    • 1970-01-01
    • 2014-08-16
    • 1970-01-01
    相关资源
    最近更新 更多