web-dev-qa-db-ja.com

iOSで既に実行中のタスクにbeginBackgroundTaskWithExpirationHandlerを使用する方法

私のアプリでは、iPhoneからサーバーに画像をアップロードしています。ユーザーがホームボタンを押すと同期中に、アプリが閉じます。

同期が完了するまでアプリをバックグラウンドで実行する必要があります。

私の質問は、「beginBackgroundTaskWithExpirationHandler」で現在実行中のタスクを追加するにはどうすればよいですか?

あなたのアイデアを共有してください。

38
Vardhan

バックグラウンドタスクハンドラーの以下のコードを使用しました。

__block UIBackgroundTaskIdentifier backgroundTaskIdentifier = [[UIApplication sharedApplication] beginBackgroundTaskWithExpirationHandler:^{

    NSLog(@"Background Time:%f",[[UIApplication sharedApplication] backgroundTimeRemaining]);

    [[UIApplication sharedApplication] endBackgroundTask:backgroundTaskIdentifier];

    backgroundTaskIdentifier = UIBackgroundTaskInvalid;
}];
15
Vardhan

その名前にもかかわらず、beginBackgroundTaskWithExpirationHandler:は実際にはタスクを「開始」しません。 「begin ....」ではなく「register ...」と考える方がいいかもしれません。それでよければ、完了したいことをしている途中だとシステムに伝えているだけです。

いくつかのポイント:

  • ほとんどすべての場合、beginBackgroundTaskWithExpirationHandler:やりたいことをやり始めたら、endBackgroundTask:完了したら。ユーザーが[ホーム]ボタンを押した時点でこれらを呼び出すことはほとんどありません(サーバーに何かを保存するなど、タスクを開始する時点でない限り)。

  • 「バックグラウンドタスク」は、一度にいくつでも開くことができます。費用はかかりません。それらはカウントを保持するようなものです。まだ1つ(「終了」に到達していない「開始」)が開いている限り、システムはその要求をより多くの時間と見なします。 「begin」メソッドと「end」メソッドの呼び出しにかかるコストは非常に少ないため、これらを使用して、終了するのに余分な時間を必要とするものをすべて囲みます。

  • 同期を完了できるかどうかを確認する方法はありません。これらのメソッドはリクエストを行うだけです。 OSは依然としてその要求を拒否する場合があります。 OSは、追加のリソースが必要になるといつでもあなたを殺してしまうかもしれません。 OSは無限に忍耐強くはありません。時間がかかりすぎた場合(通常は約10分)、期限切れハンドラーを呼び出した後、いずれにせよ殺されます。電話がオフになっているため、OSがあなたを殺すことがあります。アプリは、終了しないことに対処できる必要があります。 OSはこの機能を提供するだけなので、ユーザーエクスペリエンスを向上させることができます。

99
Rob Napier

バックグラウンドで行われるメソッドの呼び出しなど、beginBackgroundTaskWithExpirationHandlerの使用方法を次に示します。これには、新しい非同期タスクを開始するためのコードが含まれます。したがって、質問で尋ねられているものと正確には一致しません。以下のコードを追加して、誰かがこのシナリオを探しているかもしれないと考えてください:

- (void)didReceiveObjList:(CFDataRef)message
{
  // Received Object List. Processing the list can take a few seconds.
  // So doing it in separate thread with expiration handler to ensure it gets some time to do     the task even if the app goes to background.

  UIApplication *application = [UIApplication sharedApplication];
  __block UIBackgroundTaskIdentifier bgTask = [application beginBackgroundTaskWithExpirationHandler:^{
    [application endBackgroundTask:bgTask];
    bgTask = UIBackgroundTaskInvalid;
}];

  dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
      [self processObjList:message];

    [application endBackgroundTask:bgTask];
    bgTask = UIBackgroundTaskInvalid;
  });

  NSLog(@"Main Thread proceeding...");

}
9
Joe M

このページをご覧くださいApple iOSアプリケーションがバックグラウンドにある間、中断されないようにする方法を説明してください。 Apple Documentation

そして、ここに私が実装したものがあります:

UIBackgroundTaskIdentifier bgTask;

- (void)applicationDidEnterBackground:(UIApplication *)application
{
    // Use this method to release shared resources, save user data, invalidate timers, and store enough application state information to restore your application to its current state in case it is terminated later. 
    // If your application supports background execution, this method is called instead of applicationWillTerminate: when the user quits.
    NSLog(@"=== DID ENTER BACKGROUND ===");
    bgTask = [[UIApplication  sharedApplication] beginBackgroundTaskWithExpirationHandler:^{
        NSLog(@"End of tolerate time. Application should be suspended now if we do not ask more 'tolerance'");
        // [self askToRunMoreBackgroundTask]; This code seems to be unnecessary. I'll verify it.
    }];

    if (bgTask == UIBackgroundTaskInvalid) {
        NSLog(@"This application does not support background mode");
    } else {
        //if application supports background mode, we'll see this log.
        NSLog(@"Application will continue to run in background");  
    }
}

/*

- (void)askToRunMoreBackgroundTask
{
    [[UIApplication sharedApplication] endBackgroundTask:bgTask];
    NSLog(@"restart background long-running task");
    bgTask = [[UIApplication  sharedApplication] beginBackgroundTaskWithExpirationHandler:^{
        NSLog(@"End of tolerate time. Application should be suspended now if we do not ask more 'tolerance'");
        [self askToRunMoreBackgroundTask]; 
    }];

}

*/

- (void)applicationWillEnterForeground:(UIApplication *)application
{
    NSLog(@"=== GOING BACK FROM IDLE MODE ===");
    //it’s important to stop background task when we do not need it anymore
    [[UIApplication sharedApplication] endBackgroundTask:UIBackgroundTaskInvalid];  
}
6
Duyen-Hoa

以下は、アプリがバックグラウンドに移行するときに使用している他の回答のわずかなバリエーションで、メインスレッドで何かをする必要があります。

- (void)applicationWillResignActive:(UIApplication *)application
{
     UIApplication *app = [UIApplication sharedApplication];

    // Register auto expiring background task
    __block UIBackgroundTaskIdentifier bgTaskId =
        [app beginBackgroundTaskWithExpirationHandler:^{
        [app endBackgroundTask:bgTaskId];
        bgTaskId = UIBackgroundTaskInvalid;
    }];

    // Execute background task on the main thread
    dispatch_async( dispatch_get_main_queue(), ^{
        // ------------------
        // DO SOMETHING INVOLVING THE WEBVIEW - WHICH MUST OCCUR ON THE MAIN THREAD!
        // ------------------
        [app endBackgroundTask:bgTaskId];
        bgTaskId = UIBackgroundTaskInvalid;
    });
}
4
BuvinJ

beginBackgroundTaskWithExpirationHandlerの後にタスクを実行するか、アプリケーションがアプリの状態をバックグラウンドに変更したときに通知をキャプチャしてから、現在のタスクをキャンセルしてbeginBackgroundTaskWithExpirationHandlerで再度実行する必要があります。

1
tikhop