2013/12/02

iOS の task completion について

TMPTaskCompletionManagerの公開に伴い、task completionについてまとめてみます。

task completion は iOS4 で導入されたマルチタスク処理方式の一つです。アプリがバックグラウンドモードへ入った後も、実行中の処理を継続したいときに使います。実行可能時間は、iOS6までは最長10分間。iOS7では最長3分間へ短縮されました。

バックグラウンドでの実行要求は、
- [UIApplication beginBackgroundTaskWithExpirationHandler:]
で行います。要求は何度でも可能です。
処理が終了したなど、バックグラウンドでの実行が不要になった場合には、
- [UIApplication endBackgroundTask:]
を呼び出さなければなりません。このメソッドを呼び出すか、規定時間が経過した場合、アプリケーションはバックグラウンド処理を終了し、サスペンド状態へ入ります。

StackOverflowでも言及されているように、beginBackgroundTaskWithExpirationHandler:は、名前に「begin」と付いているものの、実際にはバックグラウンド処理を開始しません。このメソッドは、OSに対し、バックグラウンドでの処理継続を要求するために使います。

beginBackgroundTaskWithExpirationHandler:が呼び出される毎に、OSはその回数を記録します(動作可能時間は初回要求時から増えません)。この数値が1以上である場合、OSは、アプリがバックグラウンド処理を要求していると判断します。この回数は、endBackgroundTask:で減算されます。0になった時点で、OSはアプリケーションはバックグラウンド処理を望んでいないと判断します。メモリ管理のリファレンスカウントと同じ原理です。

言い換えれば、endBackgroundTask:を呼び出すまで、アプリケーションはバックグラウンド処理を要求し続けていることになります。例えば、アプリがフォアグラウンドへ復帰するなど、バックグラウンド処理が不要になった場合には、明示的にendBackgroundTask:を呼びださなければなりません。

Appleが公開しているiOS App Programming GuideExecuting a Finite-Length Task in the Backgroundでは言及されていないためか、「Cocoaの日々」「[iOS] バックグラウンド実行見本(Task Completion)」などを除いて、あまりフォアグラウンド復帰時の処理に関し、言及していないように感じています。

この動作を確認するために TaskCompletionSample を作成しました。

https://github.com/n-miyo/TaskCompletionSample

このアプリはアプリケーションバッジを1秒ごとに増加させます。beginBackgroundTaskWithExpirationHandler:の実行はviewDidLoadで行われ、endBackgroundTask:は実行されません。

アプリをバックグラウンドへ落とすと、バッジの増加を通じてバックグラウンド処理を確認できます。アプリを一度フォアグラウンドへ復帰させ、再度バックグラウンドへ遷移させてみます。この処理の過程では新たにbeginBackgroundTaskWithExpirationHandler:を呼び出していませんが、アプリはバックグラウンド実行を継続可能です。

画面の「clear at 'didBecomeActive'」スイッチをONにすると、- [AppDelegate applicationDidBecomeActive:]実行時、endBackgroundTask:を実行するようになります。
この状態で、一度アプリをバックグラウンドへ遷移させ、再度フォアグラウンドへ遷移させた後、再度アプリをbeginBackgroundTaskWithExpirationHandler:へ移行させると、endBackgroundTask:との呼び出し回数が釣り合い、以後バックグラウンドでの実行は行われません。

さて、バックグラウンド処理を要求する場合、時間切れ時に実行されるタスクを登録し、その中でもendBackgroundTask:を実行する必要があります。これらは定形で書けますが、毎回書くのは面倒なものです。

また、複数のタスクをバックグラウンド登録した場合、それらをフォアグラウンド復帰のタイミングで一斉にキャンセルするには、すべてのタスクIDを管理しなければなりません。

これらの比較的定形でありながら、毎回書くのが面倒になるコードをまとめたものが、先に公開した TMPTaskCompletionManagerになります。使い方などは、「iOS の task completion をサポートする TMPTaskCompletionManager を公開しました」を御覧ください。

ご意見などお待ちしております。

2 件のコメント:

  1. 詳細のご説明、サンプルのソースコード、ありがとうございます。
    beginBackgroundTaskWithExpirationHandler は、IOS7以後は、180秒だけの処理時間が与えられるそうです。

    Backgroundで処理を継続するには、何か方法がありますか?

    サンプルのソースを見ましたが、600の数字があります。このプログラムはIOS8以後でも実行可能ですか?

    お手数ですが、宜しくお願い致します。

    返信削除
  2. 古い記事にも関わらず参照頂き、どうも有難うございます。

    おっしゃる通り、iOS7 では、Background Fetch などの各種 Background Mode が整備されると同時に、バックグラウンドでの実行可能時間が180秒へ短縮されました。

    バックグラウンド実行に関しては、下記の「Background Execution」の項で言及されていますが、特に当時から大きな変更はないように思えます。

    https://developer.apple.com/library/ios/documentation/iPhone/Conceptual/iPhoneOSProgrammingGuide/BackgroundExecution/BackgroundExecution.html

    ご指摘の「600」秒はiOS6までOS検証用の値で、iOS7以降は180秒で切れてしまう筈です。すみません。

    返信削除