次の方法で共有


タスクを使用した iOS バックグラウンド処理

iOS 上でバックグラウンド処理を実行する最も簡単な方法は、バックグラウンド処理の要件を複数のタスクに分割し、それらのタスクをバックグラウンドで実行することです。 タスクには厳しい時間制限があり、アプリケーションがバックグラウンドに移行した後の処理時間は、通常、iOS 6 では約 600 秒 (10 分)、iOS 7 以降では 10 分未満です。

バックグラウンド タスクは、次の 3 つのカテゴリに分類できます。

  1. バックグラウンドセーフ タスク - アプリケーションがバックグラウンドに移行した場合に中断したくないタスクがあるアプリケーション内の任意の場所で呼び出されます。
  2. DidEnterBackground タスク - クリーンアップと状態保存を支援するために、DidEnterBackground アプリケーション ライフサイクル メソッド中に呼び出されます。
  3. バックグラウンド転送 (iOS 7 以降) - iOS 7 上でネットワーク転送を実行するために使われる特殊な種類のバックグラウンド タスク。 通常のタスクとは異なり、バックグラウンド転送には事前に決められた時間制限がありません。

バックグラウンドセーフと DidEnterBackground の各タスクは、若干の違いはありますが、iOS 6 と iOS 7 の両方で問題なく使用できます。 これら 2 種類のタスクをさらに詳しく調べてみましょう。

バックグラウンドセーフ タスクの作成

一部のアプリケーションには、アプリケーションの状態が変化した場合に、iOS によって中断されないようにする必要があるタスクがあります。 これらのタスクを中断されないように保護する方法の 1 つは、それらを実行時間の長いタスクとして iOS に登録することです。 ユーザーがアプリをバックグラウンドに配置した場合にタスクを中断したくないアプリケーション内であれば、このパターンをどこでも使用できます。 このパターンの最適な候補は、新しいユーザーの登録情報をサーバーに送信する、ログイン情報を確認するなどのタスクです。

次のコード スニペットは、バックグラウンドで実行するタスクの登録を示しています。

nint taskID = UIApplication.SharedApplication.BeginBackgroundTask( () => {});

//runs on main or background thread
FinishLongRunningTask(taskID);

UIApplication.SharedApplication.EndBackgroundTask(taskID);

登録プロセスでは、タスクを一意識別子 taskID とペアにしてから、一致する BeginBackgroundTaskEndBackgroundTask の呼び出しでラップします。 この識別子を生成するために、UIApplication オブジェクトに対して BeginBackgroundTask メソッドを呼び出してから、実行時間の長いタスクを (通常は新しいスレッドで) 開始します。 タスクが完了したら、EndBackgroundTask を呼び出し、同じ識別子を渡します。 BeginBackgroundTask 呼び出しに一致する EndBackgroundTask がない場合、iOS はアプリケーションを終了するため、これは重要です。

重要

バックグラウンドセーフ タスクは、アプリケーションのニーズに応じて、メイン スレッドまたはバックグラウンド スレッドで実行できます。

DidEnterBackground 中のタスクの実行

実行時間の長いタスクをバックグラウンドセーフにするだけでなく、アプリケーションがバックグラウンドに配置されたときにタスクを開始するために登録を使用できます。 iOS の AppDelegate クラスには DidEnterBackground というイベント メソッドが用意されています。これを使うと、アプリケーションがバックグラウンドに移行する前にアプリケーションの状態を保存し、ユーザー データを保存し、機密コンテンツを暗号化することができます。 アプリケーションは約 5 秒でこのメソッドから戻りますが、戻らない場合は終了されます。 そのため、完了までの時間が 5 秒を超える可能性のあるクリーンアップ タスクは、DidEnterBackground メソッド内から呼び出すことができます。 これらのタスクは別のスレッドで呼び出す必要があります。

このプロセスは、実行時間の長いタスクを登録する場合と同じです。 次のコード スニペットは、この動作を示しています。

public override void DidEnterBackground (UIApplication application) {
  nint taskID = UIApplication.SharedApplication.BeginBackgroundTask( () => {});
  new Task ( () => {
    DoWork();
    UIApplication.SharedApplication.EndBackgroundTask(taskID);
  }).Start();
}

まず、AppDelegateDidEnterBackground メソッドをオーバーライドし、前の例で行ったように、BeginBackgroundTask を介してタスクを登録します。 次に、新しいスレッドを生成し、実行時間の長いタスクを実行します。 DidEnterBackground メソッドは既に戻っているため、EndBackgroundTask の呼び出しは実行時間の長いタスクの内部から行われることに注意してください。

重要

iOS は、ウォッチドッグ メカニズムを使って、アプリケーションの UI の応答性を確保しています。 DidEnterBackground 内で長時間を費やすアプリケーションは、UI で応答しなくなります。 バックグラウンドで実行するタスクを開始すると、DidEnterBackground が適時に戻り、UI の応答性を維持し、ウォッチドッグによるアプリケーションの強制終了を防ぐことができます。

バックグラウンド タスクの時間制限の処理

iOS は、バックグラウンド タスクの実行時間に厳しい制限を設けているため、割り当てられた時間内に EndBackgroundTask の呼び出しが行われない場合、アプリケーションは終了します。 残りのバックグラウンド時間を追跡し、必要に応じて有効期限ハンドラーを使うことで、iOS によるアプリケーションの終了を回避できます。

バックグラウンドの残り時間へのアクセス

タスクが登録されているアプリケーションがバックグラウンドに移行すると、登録されたタスクの実行には約 600 秒かかります。 UIApplication クラスの静的な BackgroundTimeRemaining プロパティを使って、タスクが完了するまでにかかる時間を確認できます。 次のコードを使うと、バックグラウンド タスクの残り時間が秒単位で示されます。

double timeRemaining = UIApplication.SharedApplication.BackgroundTimeRemaining;

有効期限ハンドラーを使ったアプリ終了の回避

BackgroundTimeRemaining プロパティにアクセス権を付与するだけでなく、iOS には、有効期限ハンドラーを使ってバックグラウンド時間の期限切れを適切に処理する方法が用意されています。 これは、タスクに割り当てられた時間が期限切れになるときに実行される省略可能なコード ブロックです。 有効期限ハンドラーのコードは、EndBackgroundTask を呼び出してタスク ID を渡します。これはアプリが正常に動作していることを示し、タスクの期限切れになっても iOS がアプリを終了しないようにするものです。 EndBackgroundTask は、通常の実行過程だけでなく、有効期限ハンドラー内でも呼び出す必要があります。

以下に示すように、有効期限ハンドラーはラムダ式を使った匿名関数として表現されます。

Task.Factory.StartNew( () => {

    //expirationHandler only called if background time allowed exceeded
    var taskId = UIApplication.SharedApplication.BeginBackgroundTask(() => {
        Console.WriteLine("Exhausted time");
        UIApplication.SharedApplication.EndBackgroundTask(taskId); 
    });
    while(myFlag == true)
    {
        Console.WriteLine(UIApplication.SharedApplication.BackgroundTimeRemaining);
        myFlag = SomeCalculationNeedsMoreTime();
    }
    //Only called if loop terminated due to myFlag and not expiration of time
    UIApplication.SharedApplication.EndBackgroundTask(taskId);
});

コードの実行に有効期限ハンドラーは必要ありませんが、バックグラウンド タスクでは常に有効期限ハンドラーを使う必要があります。

iOS 7 以降のバックグラウンド タスク

バックグラウンド タスクに関する iOS 7 の最大の変更点は、タスクの実装方法ではなく、実行されるタイミングです。

iOS 7 より前の場合、バックグラウンドで実行されているタスクが完了するまでの時間は 600 秒だったことを思い出してください。 この制限の理由の 1 つは、バックグラウンドで実行されているタスクにより、タスクの実行中はデバイスが起動したままになるためです。

Graph of the task keeping the app awake pre-iOS 7

iOS 7 のバックグラウンド処理は、バッテリー寿命を延ばすために最適化されています。 iOS 7 では、バックグラウンド処理が日和見的になります。デバイスを起動したままにするのではなく、タスクはデバイスがスリープ状態になるタイミングを尊重し、代わりに、デバイスが起動したときに処理をまとめて実行し、電話、通知、メールの受信、その他の一般的な中断を処理します。 次の図は、タスクがどのように分割されるかを示しています。

Graph of the task being broken into chunks post-iOS 7

タスクの実行時間は連続的ではなくなるため、iOS 7 では、ネットワーク転送を実行するタスクを異なる方法で処理する必要があります。 開発者には、ネットワーク転送を処理するために NSURlSession API を使うことをお勧めします。 次のセクションは、バックグラウンド転送の概要です。

バックグラウンド転送

iOS 7 のバックグラウンド転送のバックボーンは、新しい NSURLSession API です。 NSURLSession を使うと、次のタスクを作成できます。

  1. ネットワークやデバイスの中断があってもコンテンツを転送する。
  2. 大きなファイルのアップロードとダウンロード ("バックグラウンド転送サービス")。

このしくみを詳しく見てみましょう。

NSURLSession API

NSURLSession は、ネットワーク経由でコンテンツを転送するための強力な API です。 ネットワークの中断やアプリケーションの状態の変化があってもデータ転送を処理できるツールのセットが用意されています。

NSURLSession API を使うと、1 つ以上のセッションを作成し、その結果としてネットワーク経由で関連データのブロックを往復させるタスクを生成できます。 タスクは非同期で実行され、データは迅速かつ確実に転送されます。 NSURLSession は非同期であるため、すべてのセッションには、転送の完了をシステムとアプリケーションに通知するための完了ハンドラー ブロックが必要です。

iOS 7 より前と iOS 7 以降の両方で有効なネットワーク転送を実行するには、NSURLSession が転送をキューに登録できるかどうかを確認し、そうでない場合は通常のバックグラウンド タスクを使って転送を実行します。

if ([NSURLSession class]) {
  // Create a background session and enqueue transfers
}
else {
  // Start a background task and transfer directly
  // Do NOT make calls to update the UI here!
}

重要

iOS 6 はバックグラウンド UI 更新をサポートしておらず、アプリケーションが終了するため、iOS 6 準拠のコードでバックグラウンドから UI を更新する呼び出しを行わないでください。

NSURLSession API には、認証の処理、失敗した転送の管理、サーバー側ではなくクライアント側のエラーの報告を行うための豊富な機能セットが含まれています。 これは、iOS 7 で導入されたタスク実行時の中断を埋めるのに役立ちます。また、大きなファイルを迅速かつ確実に転送するためのサポートも提供されます。 次のセクションでは、この 2 つ目の機能について説明します。

バックグラウンド転送サービス

iOS 7 より前では、バックグラウンドでのファイルのアップロードまたはダウンロードは信頼できませんでした。 バックグラウンド タスクの実行時間は限られていますが、ファイルの転送にかかる時間はネットワークとファイルのサイズによって異なります。 iOS 7 では、NSURLSession を使って大きなファイルを問題なくアップロードおよびダウンロードできます。 バックグラウンドで大きなファイルのネットワーク転送を処理する特定の NSURLSession セッションの種類は、"バックグラウンド転送サービス" と呼ばれます。

バックグラウンド転送サービスを使って開始された転送はオペレーティング システムによって管理され、認証とエラーを処理する API が用意されています。 転送には任意の時間制限の制約を受けないため、大きなファイルのアップロードまたはダウンロード、バックグラウンドでのコンテンツの自動更新などに使用できます。 サービスの実装方法の詳細については、バックグラウンド転送のチュートリアルを参照してください。

バックグラウンド転送サービスは、バックグラウンド フェッチまたはリモート通知と組み合わせて、アプリケーションがバックグラウンドでコンテンツを更新できるようにすることがよくあります。 次の 2 つのセクションでは、iOS 6 と iOS 7 の両方でバックグラウンドで実行するようにアプリケーション全体を登録する概念について説明します。