Backgroundはアプリのライフサイクル状態で、実行を継続しますが画面には表示されません。iOSおよびAndroidでのバックグラウンド作業の基礎を説明します:制限、タイムアウト、beginBackgroundTask、WorkManager、Serviceによるバックグラウンドタスク、およびBackgroundの正しい処理のためのベストプラクティス。
重要ポイント
Backgroundはアプリがオペレーティングシステムに存在し続け、コードを実行しリソースを消費しますが、デバイス画面に表示されない状態です。ユーザーはホーム画面、別のアプリにいるか、デバイスの画面がロックされています。iOSでは、BackgroundはInactiveの後に続きます — 遷移チェーン:Active → Inactive → Background。Androidでは、onStopがActivityのBackgroundへの遷移を示します。
両プラットフォームともバックグラウンド作業に厳しい制限を課しています。iOSはBackgroundに入った後、コード実行に限られた時間(通常30秒)を提供し、その後アプリはSuspendedに移行します。Androidはより柔軟で、可視通知付きのForeground Serviceは無制限に実行できますが、通常のBackground Serviceは数分に制限されています。開発者の主要なタスクは、状態を適切に保存し、システムのバックグラウンドタスクAPIを介して作業の継続をスケジュールすることです。
システムはメモリが不足すると、いつでもバックグラウンドアプリを終了できます。終了時、保存されていないデータはすべて失われます。したがって、applicationDidEnterBackground(iOS)またはonStop(Android)で状態を保存することが非常に重要です。終了後、次の起動はNot Runningからコールドスタートで始まり、保存された状態を復元します。
BackgroundとSuspendedを区別することが重要です。Background — アプリがアクティブにコードを実行しています。Suspended — アプリはメモリにありますがコードを実行していません — 凍結されています。iOSでは、アプリはバックグラウンドタスク完了後にBackgroundからSuspendedに移行します。AndroidにはSuspendedはなく、プロセスは存在する(Backgroundを含む)か終了(Not Running)かのいずれかです。ただし、AndroidはLMK(Low Memory Killer)を介してスレッド実行を一時停止できます。
| 特性 | iOS Background | Android Background |
|---|---|---|
| コード実行 | はい、最大30秒 | はい、APIに依存 |
| UI表示 | いいえ | いいえ |
| デフォルトタイムアウト | ~30秒(beginBackgroundTask) | 数分(Service) |
| 無制限作業 | 特別カテゴリのみ(オーディオ、VoIP、ナビ) | 通知付きForeground Service |
| 実行保証 | なし — システムがいつでも終了可 | WorkManagerが実行を保証 |
| 権限必要 | はい — Info.plistのcapabilities | はい — FOREGROUND_SERVICE権限 |
| 次の状態 | Suspended → Not Running | Not Running(または再起動) |
iOSでは、BackgroundはデリゲートメソッドapplicationDidEnterBackgroundを介して処理されます。このメソッドで、開発者はユーザー状態を保存し、リソースを解放し、バックグラウンドタスクを完了する必要があります。Backgroundに入った後にコードを実行するには、beginBackgroundTask(expirationHandler:)を使用します — システムに追加時間(通常30秒)を要求するAPIです。この時間内にタスクが完了しない場合、expirationHandlerが呼び出され、アプリは強制的にSuspendedに移行します。
iOS 13で、AppleはBGTaskSchedulerを導入しました — バックグラウンドタスクをスケジュールするための最新APIです。バックグラウンドに入った後の終了のみに時間を与えるbeginBackgroundTaskとは異なり、BGTaskSchedulerは将来のタスク実行をスケジュールできます — 例えば、1時間に1回のコンテンツ更新や夜間の分析アップロードなど。BGTaskSchedulerは新しいプロジェクトに推奨されるアプローチであり、バッテリー効率が優れています。
import UIKit
import BackgroundTasks
@main
class AppDelegate: UIResponder, UIApplicationDelegate {
var backgroundTaskID: UIBackgroundTaskIdentifier = .invalid
// アプリがバックグラウンドに移行 — バックグラウンドタスク開始
func applicationDidEnterBackground(_ application: UIApplication) {
saveAppState()
startBackgroundTask()
}
private func startBackgroundTask() {
backgroundTaskID = UIApplication.shared.beginBackgroundTask { [weak self] in
// 時間切れ — 強制終了
self?.endBackgroundTask()
}
// バックグラウンド作業をシミュレート(サーバーにデータ保存)
DispatchQueue.global().async { [weak self] in
uploadAnalyticsData()
self?.endBackgroundTask()
}
}
private func endBackgroundTask() {
guard backgroundTaskID != .invalid else { return }
UIApplication.shared.endBackgroundTask(backgroundTaskID)
backgroundTaskID = .invalid
}
// BGTaskScheduler登録
func application(
_ application: UIApplication,
didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?
) -> Bool {
BGTaskScheduler.shared.register(
forTaskWithIdentifier: "com.example.refresh",
using: nil
) { task in
handleAppRefresh(task: task as! BGAppRefreshTask)
}
return true
}
func scheduleAppRefresh() {
let request = BGAppRefreshTaskRequest(identifier: "com.example.refresh")
request.earliestBeginDate = Date(timeIntervalSinceNow: 3600)
try? BGTaskScheduler.shared.submit(request)
}
func handleAppRefresh(task: BGAppRefreshTask) {
scheduleAppRefresh()
task.expirationHandler = { task.setTaskCompleted(success: false) }
fetchLatestData { result in
task.setTaskCompleted(success: result)
}
}
}コードはiOSでの完全なBackground処理を示しています。applicationDidEnterBackgroundは、タイムアウトとexpirationHandler付きでbeginBackgroundTaskを介してバックグラウンドタスクを開始します。同時に、BGTaskSchedulerが定期的なコンテンツ更新のために登録されます。beginBackgroundTaskは即時シャットダウンタスクに使用され、BGTaskSchedulerは長期計画に使用されます。両方のAPIはタスク識別子の適切な管理を必要とします。
Androidでは、Backgroundは複数のAPIを介して管理されます。従来のServiceはバックグラウンドでのコード実行を許可しますが、Android 8+(API 26)以降、Background Serviceは制限されています:アプリがバックグラウンドに入って数分後にシステムが終了します。永続的通知付きのForeground Serviceは無制限に実行できます。WorkManagerは、デバイス再起動後でも実行保証付きのバックグラウンドタスクに推奨されるソリューションです。
AndroidはiOSとは異なり、長時間実行されるバックグラウンドプロセスをサポートしています。Foreground Serviceはユーザーが確認すべきタスク(音楽再生、ナビゲーション、ワークアウト追跡)に使用されます。JobSchedulerとWorkManagerは遅延可能なタスク(データ同期、ログアップロード、キャッシュ更新)に使用されます。主な違い:Androidは条件付き(Wi-Fi、充電中、デバイスアイドル)でタスクをスケジュールでき、バッテリーとトラフィックを節約します。
import android.app.Service
import android.content.Intent
import android.os.IBinder
import androidx.work.*
// 1. 長時間バックグラウンド作業のForeground Service
class SyncService : Service() {
override fun onStartCommand(intent: Intent?, flags: Int, startId: Int): Int {
val notification = createNotification()
startForeground(NOTIFICATION_ID, notification)
performBackgroundWork()
return START_STICKY
}
private fun performBackgroundWork() {
Thread {
// サーバーとのデータ同期
syncDataToServer()
stopForeground(STOP_FOREGROUND_REMOVE)
stopSelf()
}.start()
}
override fun onBind(intent: Intent?): IBinder? = null
}
// 2. 遅延バックグラウンドタスクのWorkManager
class DataSyncWorker(
private val context: Context,
private val params: WorkerParameters
) : CoroutineWorker(context, params) {
override suspend fun doWork(): Result {
return try {
// サーバーへの分析アップロード
uploadAnalytics()
Result.success()
} catch (e: Exception) {
if (runAttemptCount < 3) Result.retry() else Result.failure()
}
}
}
// WorkManagerタスクスケジューリング
fun scheduleBackgroundSync(context: Context) {
val constraints = Constraints.Builder()
.setRequiredNetworkType(NetworkType.CONNECTED)
.setRequiresBatteryNotLow(true)
.build()
val request = OneTimeWorkRequestBuilder<DataSyncWorker>()
.setConstraints(constraints)
.setBackoffCriteria(BackoffPolicy.EXPONENTIAL, 30, TimeUnit.SECONDS)
.build()
WorkManager.getInstance(context).enqueue(request)
}コードはAndroidでのバックグラウンド作業の2つのアプローチを示しています。SyncService — 即時および長時間のバックグラウンド作業のための通知付きForeground Service。DataSyncWorker — 条件付き(Wi-Fi、充電)の遅延タスクのためのWorkManager。WorkManagerはデバイス再起動後でも実行を保証し、再試行の指数バックオフをサポートします。Foreground Serviceはステータスバーに永続的通知が必要です。
両モバイルプラットフォームともバックグラウンド作業ルールを常に強化しています。iOSでは、OSの新しい世代ごとにバックグラウンド実行時間が短縮され、新しい制限が追加されます。Androidでは、Googleはますます厳格な省電力モード(Doze、App Standby)を導入しています。開発者は現在の制限を把握しておく必要があります。そうしないと、アプリがシステムによって早期に終了される可能性があります。
iOSでは、iOS 13以降、バックグラウンド時間を乱用するアプリに対してシステムがバックグラウンドタスクを無効にします。各アプリはユーザーの行動に基づいて特定の制限を受けます。BGTaskSchedulerは最適な時間(例:デバイスがWi-Fiに接続され充電中)に実行をスケジュールします。BGTaskSchedulerを正しく使用するアプリはより多くのバックグラウンド時間を得られます。
Androidでは、Android 9(API 28)以降、バックグラウンド作業はDozeモードによって制限され、デバイスがアイドル状態のときにアクティブになります。Doze中のアプリはバックグラウンドタスクを実行できず、ネットワークは切断され、JobSchedulerとWorkManagerはDoze終了までタスクを延期します。Foreground ServiceがDozeを回避する唯一の方法ですが、乱用するとユーザーによるアプリのブロックと権限の取り消しにつながります。
| 制限 | iOS | Android |
|---|---|---|
| バックグラウンドタスクタイムアウト | ~30秒(beginBackgroundTask) | 数分(JobScheduler) |
| 無制限バックグラウンド | オーディオ、VoIP、ナビ、Bluetooth | Foreground Service + 通知 |
| 省電力 | Low Power Mode — バックグラウンドタスク無効化 | Doze、App Standby、バッテリー最適化 |
| スケジューリング | BGTaskScheduler(iOS 13+) | WorkManager(Android Jetpack) |
| 再起動後 | プッシュ通知のみ | WorkManagerがタスクを保持 |
| 最大実行時間 | ~30分(オーディオ) | 無制限(Foreground Service) |
第一のルール — バックグラウンドでのリソース消費を最小限にします。ほとんどのバックグラウンドタスクは、デバイスが充電中でWi-Fiに接続されている時間に延期できます。条件付きタスクのスケジュールにはBGTaskScheduler(iOS)とWorkManager(Android)を使用してください。バックグラウンドで重い計算を実行しないでください — バッテリーを消耗し、CPUスロットリングを引き起こします。
第二のルール — beginBackgroundTaskには常にexpirationHandlerを指定してください。アプリが割り当てられた時間内にタスクを完了しない場合、システムは強制的にSuspendedに移行するか終了します。expirationHandlerはデータを保存し作業を適切に終了する最後のチャンスです。Androidでは、WorkManagerでsetForegroundAsyncを使用して、より多くの時間が必要な場合に通常のタスクをフォアグラウンドに変換します。
第三のルール — 起動前にバックグラウンド作業の制限を確認してください。iOSでは、UIApplication.shared.backgroundTimeRemainingを使用して残り時間を確認します。Androidでは、ActivityManager.isBackgroundRestricted()を確認 — trueの場合、アプリはバックグラウンドタスクを実行できないため、設定で制限を解除するようユーザーに提案します。これは重要なバックグラウンド機能を持つアプリにとって特に重要です — アラーム、カレンダー、同期。
第四のルール — 実際のデバイスでバックグラウンドタスクをテストしてください。シミュレータとエミュレータは実際のバックグラウンド作業制限を再現しません。iOSでは、XcodeでDebug → Simulate Background Fetchを使用します。Androidでは、再起動後のWorkManagerテストにadb shell am broadcast -a android.intent.action.ACTION_BOOT_COMPLETEDを使用します。バッテリー残量の少ないデバイスでの実際のテストが、ほとんどのバックグラウンド作業の問題を明らかにします。
import UIKit
final class BackgroundTaskManager {
static let shared = BackgroundTaskManager()
private var tasks: [String: UIBackgroundTaskIdentifier] = [:]
func startTask(name: String, expiration: @escaping () -> Void) {
let remaining = UIApplication.shared.backgroundTimeRemaining
print("バックグラウンド残り時間: \(remaining)秒")
let task = UIApplication.shared.beginBackgroundTask { [weak self] in
print("タスクの時間切れ: \(name)")
expiration()
self?.endTask(name: name)
}
tasks[name] = task
}
func endTask(name: String) {
guard let task = tasks.removeValue(forKey: name),
task != .invalid
else { return }
UIApplication.shared.endBackgroundTask(task)
}
}コードは残り時間を追跡し識別子を管理するバックグラウンドタスクマネージャーを示しています。backgroundTimeRemainingは強制終了前の秒数を返します — 値が無限の場合、アプリは制限なく実行されています(オーディオ、ナビ)。マネージャーは異なる名前で複数のバックグラウンドタスクを起動し、それぞれを適切に完了できます。このアプローチはバックグラウンドタスクのリークを防ぎ、未完了のタスクのためにシステムがアプリを終了しないようにします。
よくある質問
はい、限られたカテゴリでのみ可能です:オーディオ(AVAudioSessionカテゴリ.playback)、VoIP(PushKit)、ナビゲーション(CLLocationManagerとallowsBackgroundLocationUpdates)、Bluetooth(central background mode)、バックグラウンド更新(BGTaskScheduler)。その他すべては最大30秒です。iOS 16+では、Appleは許可されたカテゴリでも要件を強化しました。
beginBackgroundTaskは、バックグラウンドに入った後にアプリの寿命を約30秒延ばす同期APIです。applicationDidEnterBackgroundで呼び出されます。BGTaskSchedulerは、システムトリガー(時間、場所、コンテンツ更新)を介して将来のタスクをスケジュールする非同期APIです。BGTaskSchedulerはAppleがiOS 13+に推奨する最新のアプローチです。
Android 8(API 26)以降、Background Serviceはアプリがバックグラウンドに入ってから数分後に終了します。解決策:長時間の操作には通知付きのForeground Service、遅延タスクにはWorkManagerを使用してください。設定でアプリのバッテリー最適化を確認 — 最適化されている場合、システムはバックグラウンドタスクを遅延またはキャンセルする可能性があります。
Cmd+Shift+Hを押してホーム画面に移動します。Xcodeで、Debug → Simulate Background Fetchを使用します。beginBackgroundTaskを確認するには、コンソール(Shift+Cmd+C)を開きe UIApplication.shared.backgroundTimeRemainingを呼び出します。Xcode 15+では、シミュレータのDiagnosticsタブでBackground Executionシナリオが利用可能です。
Process Deathは、リソース不足またはバックグラウンドでのアイドル時にシステムがAndroidプロセスを終了することです。iOSとは異なり、AndroidにはSuspendedはなく、プロセスは生きている(バックグラウンドにできる)か死んでいる(Not Running)かのいずれかです。Process DeathはOSの通常の動作であり、アプリはSavedStateHandle、onSaveInstanceState、またはDataStoreを介して状態を適切に復元する必要があります。
まとめ
ターンキー方式のモバイルアプリケーションを開発します
IT Sectrは2017年からスタートアップや企業向けにiOS・Androidアプリケーションを開発しています。私たちがご相談に乗り、最適なソリューションをご提案します。