WorkManagerとは?Androidの遅延実行の仕組みと採用判断を解説
WorkManagerは、すぐ実行しなくてよい処理をAndroid端末で確実に片づけるためのJetpackライブラリです。アプリを閉じても端末を再起動しても、登録したタスクは残り、条件がそろった時点で走ります。安定版は2.11.2(2026年3月25日公開)、次期系列の2.12.0-beta01は2026年7月29日に公開済みです。この記事では、なぜ再起動をまたいで復元できるのかという仕組みから、WorkRequestとWorkerの書き分け、制約が実行タイミングをどう遅らせるかを押さえたうえで、FCMやサーバー側キューではなくWorkManagerを選ぶ条件まで整理します。
まとめ:WorkManagerの採用判断と使いどころ
Androidアプリで「アプリが起動していなくても、いつか必ず終わらせたい処理」があるなら、既定の選択肢はWorkManagerです。ログ送信、サーバーとのデータ同期、撮影した画像のアップロード。この種の処理をコルーチンで直接投げると、画面が閉じた時点で消えます。WorkManagerは登録内容を端末内のデータベースへ書き、OSの実行基盤へ委ねる形をとるため、プロセスが死んでも予定が残ります。
一方で、秒単位の即時性が要る処理には向きません。制約や省電力の都合で、実行は数分から数時間ずれます。ユーザーの操作に即応する通知や、サーバー側の都合で今すぐ端末を動かしたい場面は、FCMの高優先度メッセージやフォアグラウンドサービスの領分です。
採用を見送ってよい条件も明確です。処理が画面の生存期間で完結する、端末側でやる必然性がなくサーバーで回せる、実行が15分より短い周期を必要とする。このいずれかに当たるなら、別の道具を選んだほうが構成は簡単になります。判断の分かれ目は「アプリが消えても続きをやる必要があるか」の一点だと考えてください。
WorkManagerの定義と確実な実行を支える永続化と復元の仕組み
WorkManagerが引き受ける処理と扱わない処理を分ける要件の境目
WorkManagerが対象とするのは、公式の言い方では「遅延可能で、かつ確実に実行したい」処理です。遅延可能とは、今すぐでなくてよいという意味。確実にとは、アプリが終了しても端末が再起動しても実行の約束が残るという意味です。この2条件がそろって初めて、このライブラリを持ち出す意味が出ます。
逆に、片方しか満たさない処理は対象外になります。画面を表示している間だけ走ればよい通信は、ViewModelのスコープで済ませるほうが素直です。即時性が必要で遅延を許さない処理も、後述するとおり別の仕組みへ回します。メインスレッドから切り離したいだけの処理を全部ここへ寄せると、DBへの書き込みとスケジューリングの往復が無駄になります。
非同期処理そのものの考え方を先に整理しておきたい場合は、同期処理と非同期処理の違いをまとめた解説を読んでおくと、この境目の判断が速くなります。
タスクをデータベースへ保存して端末の再起動後も復元する処理の流れ
確実さの正体は永続化です。enqueueしたWorkRequestは、実行条件・入力データ・リトライ回数を含めて端末内のデータベースへ書き込まれます。メモリ上のキューではないため、プロセスが強制終了されても記録は消えません。
端末を再起動すると、WorkManagerは起動完了のブロードキャストを受けてデータベースを読み直し、未完了のタスクを組み立て直します。この復元があるおかげで、「夜間に同期する」といった予定が再起動をまたいで生き残ります。裏を返せば、登録した覚えのない古いタスクが延々と残り続ける事故も起きうるということです。開発中にIDを変えながら試すと、端末内に重複した予定が積み上がります。
APIレベルごとに内部の実行基盤を切り替えて差異を吸収する構造
WorkManagerは自前でタイマーを持ちません。実際に時刻や条件を見張るのはOS側のジョブ基盤で、ライブラリはその上に載る薄い層です。歴史的にはAPIレベルに応じてJobSchedulerとAlarmManagerを使い分けてきましたが、2.11.0でminSdkがAPI 21からAPI 23へ引き上げられ、対象範囲が整理されました。
実装者から見ると、この構造の意味は2つあります。ひとつは、OS側の制限がそのまま自分のタスクに効いてくること。もうひとつは、端末やOSの版によって実行タイミングの傾向が変わること。同じコードでも、検証端末では数分で走り、別の端末では1時間待たされる場面が出てきます。
| 層 | 担当 | 備考 |
|---|---|---|
| アプリ | Worker実装 | doWorkを書く |
| WorkManager | 永続化と復元 | DBへ保存 |
| OS | 条件監視と起動 | JobScheduler |
WorkRequestとWorkerの種別で決まる実装の書き分け
OneTimeとPeriodicのWorkRequestを使い分ける基準
登録の入口は2種類です。1回だけ実行するならOneTimeWorkRequest、繰り返すならPeriodicWorkRequestを使います。判断軸は単純で、同じ処理を定期的に走らせ続けたいかどうかで決まります。
ただしPeriodicには外せない制限があります。繰り返し間隔の下限は15分で、これより短い周期は指定できません。5分ごとの同期を求められた場合、この方式では実現できず、別の設計へ切り替える判断が要ります。またPeriodicは「15分ちょうど」を保証するものでもなく、条件がそろわなければ次の窓へ持ち越されます。
val req = PeriodicWorkRequestBuilder<SyncWorker>(
15, TimeUnit.MINUTES
).setConstraints(constraints).build()
WorkerとCoroutineWorkerを選び分ける基準
処理本体を書くクラスは4種類あります。Workerは用意されたバックグラウンドスレッドで同期的に走る素朴な形。CoroutineWorkerはsuspend fun doWork()で書け、内部で中断関数をそのまま呼べます。ほかにRxJava向けのRxWorkerと、独自の非同期APIへつなぐListenableWorkerがあります。
Kotlinで新規に書くならCoroutineWorkerが既定です。Retrofitのsuspend関数もRoomの中断クエリもそのまま呼べるうえ、WorkManagerが処理を止めるときにコルーチンのキャンセルとして伝わります。Workerで同じことをやると、停止シグナルを自分でisStoppedで見張る手間を避けられません。コルーチン側の前提はsuspendと構造化並行性を整理した解説で押さえておくと、キャンセルの伝わり方まで一続きで理解できます。
なお2.10.0ではConfiguration.workerCoroutineContextが加わり、CoroutineWorkerを走らせるコンテキストをアプリ全体で指定できるようになりました。テストで差し替えたい場合の入口としても使えます。
| 種別 | 書き方 | 向く場面 |
|---|---|---|
| Worker | 同期のdoWork | 単純な処理 |
| CoroutineWorker | suspendのdoWork | Kotlin全般 |
| RxWorker | Singleを返す | RxJava資産 |
| ListenableWorker | Futureを返す | 独自の非同期 |
入出力Dataの上限を守って大きな値を安全に受け渡す実装作法
Workerとの値のやり取りにはDataを使いますが、サイズの上限があります。プロセス間でやり取りされる都合上、10KB程度を超える値は例外の対象です。画像そのものやレスポンス全文を詰めると、この壁に当たります。
回避の型は決まっています。実体はファイルやRoomのテーブルへ置き、Dataにはそのパスや主キーだけを渡す。チェーンで次のWorkerへ引き継ぐ場合も同じで、識別子を渡してWorker側が読み直す形にします。この設計にしておくと、リトライで再実行されたときにも同じ入力を再現できます。
制約とバックオフが決めるタスクの実行時刻と遅延の判断基準を見極める
5種類のConstraintsで通信量と電池を守る実行条件の絞り方
WorkManagerの実行条件はConstraintsで表します。指定できるのはネットワーク接続、充電中かどうか、バッテリー残量、ストレージ残量、端末がアイドル状態かの5種類です。条件を付けた分だけ実行は後ろへずれますが、ユーザーの通信量やバッテリーを守れます。
実務でよく使うのはネットワークと充電の組み合わせです。大きなファイルの同期ならNetworkType.UNMETEREDで従量課金の回線を避け、さらに充電中に限定して深夜へ寄せる。2.10.0で追加されたsetRequiredNetworkRequestを使えば、回線種別より細かい要件も指定できます。
val constraints = Constraints.Builder()
.setRequiredNetworkType(NetworkType.UNMETERED)
.setRequiresCharging(true)
.build()
Dozeとスタンバイバケットにより実行の遅延が長引く仕組みを解く
制約を満たしても、すぐ走るとは限りません。端末が動かされずに放置されるとOSはDozeへ入り、ジョブの実行を定期的な保守窓へまとめます。この窓は時間が経つほど間隔が開くため、放置された端末では待ち時間が数時間規模になります。
さらにアプリごとのスタンバイバケットが効きます。ユーザーが触らないアプリほど低いバケットへ落ち、ジョブの実行枠が絞られます。「テスト端末では走るのに、実機の利用者からは同期されないと言われる」という報告の多くは、この2つが原因です。OS側の省電力の仕組みはDozeモードの動作とDeep/Lightの違いを解説した記事で押さえておくと、遅延の見積もりが現実的になります。
加えて、Android 15以降はdataSync型のフォアグラウンドサービスに24時間あたり合計6時間という上限が入り、Android 16以降はフォアグラウンドサービスから開始したジョブもランタイムクォータの対象になりました。長時間の同期を常駐サービスで押し通す設計は、年々通りにくくなっています。
バックオフとリトライの既定値をエラー別に決める判断基準の定め方
Workerの戻り値は成功・失敗・再試行の3つです。Result.retry()を返すとWorkManagerがバックオフ方針に従って再登録します。方針は指数と線形の2種類で、初回の待ち時間の下限は10秒です。
ここで判断が要るのは、どのエラーを再試行に回すかです。ネットワーク断や5xxは再試行の価値がありますが、401や422のようにサーバー側が明確に拒否した応答を再試行へ回すと、無駄なリクエストを繰り返します。失敗の分類を先に決め、再試行しない側はResult.failure()で打ち切る。この切り分けを省くと、電池と通信量だけが減っていきます。
一意処理とチェーンで多重起動を防ぐ登録方針と運用設計の実務基準
ExistingWorkPolicyで重複登録を捌く4つの選択肢
同じ処理を二重に登録してしまう事故は、名前付きの一意処理で防げます。enqueueUniqueWorkに名前と方針を渡すと、同名の処理が既にある場合の振る舞いを指定できます。方針は既存を捨てて置き換える、既存を残して今回を捨てる、後ろへ追加する、失敗時のみ置き換えて追加する、の4つです。
選び方は処理の性質に応じた判断です。同期のように最新の1回だけ走ればよいものは既存を残す方針、アップロードのように取りこぼしを許さないものは追加する方針が合います。定期処理側にも同種の指定があり、間隔や制約を変えたいときは更新の方針を選べば、既存の予定を壊さずに設定だけ差し替えられます。
| 方針 | 既存がある時 | 向く処理 |
|---|---|---|
| KEEP | 今回を捨てる | 定期同期 |
| REPLACE | 既存を捨てる | 設定の反映 |
| APPEND | 後ろへ連結 | 順次アップロード |
チェーンとWorkInfoで複数処理の進行状況を追う組み立て方
複数の処理を順番に走らせたい場合は、beginWithとthenで連結します。前段が成功したときだけ次が走り、途中で失敗すれば以降は動きません。並列に走らせたい処理はリストで渡し、そろってから次段へ進む形も書けます。
進行状況の観測にはWorkInfoを使います。IDやタグ、一意処理の名前を指定して、キュー待ち・実行中・成功・失敗といった状態を受け取れます。画面へ進捗を出す場合はFlowで購読し、setProgressでWorker側から中間値を渡す形が扱いやすい構成です。
優先処理と10分を超える長時間処理で通知を出す条件と実装方法
今すぐ始めたい処理にはsetExpeditedがあります。システムの割り当てが尽きたときの振る舞いをOutOfQuotaPolicyで指定する必要があり、通常のタスクへ格下げするか、要求ごと捨てるかを選びます。無条件に速くなる仕組みではない点は押さえておいてください。
実行が10分を超えうる処理は、長時間実行として扱います。setForegroundで通知を提示し、フォアグラウンドサービスとして走らせる形です。通知が出る以上ユーザーからは見えるため、体験を壊さない文言と進捗表示を用意しておく。10分の目安を超えたまま通知を出さない実装は、途中で停止されます。
他のバックグラウンド手段と比べた採用条件と見送り場面の判断基準
JobSchedulerとAlarmManagerとの比較で決める採用の3条件
OS標準のJobSchedulerでも、条件付きのジョブ登録はできます。それでもWorkManagerを選ぶ理由は3つに絞れます。ひとつはAPIレベルごとの差異を吸収してくれること。ふたつ目はチェーンや一意処理といった運用上の道具がそろっていること。3つ目はテスト用の実装が公式に提供され、実行タイミングを操作した検証が書けることです。
AlarmManagerとの線引きはさらに明快です。あちらは「指定時刻にユーザーへ知らせる」ための仕組みで、目覚ましやカレンダー通知が本来の用途になります。時刻の正確さが要件でないなら、選ぶ理由はありません。逆に「毎朝7時ちょうど」を外せない要件なら、WorkManagerでは満たせないと判断してください。
即時性が要る処理をFCMと常駐サービスへ寄せる要件の境界を判断する
サーバー側の都合で今すぐ端末を動かしたい場合は、WorkManagerの出番ではありません。FCMによるプッシュ配信の仕組みを解説した記事にあるとおり、メッセージを受けた時点で処理を起こす形が筋です。実務では、FCMで通知を受け取り、その中で重い処理をWorkManagerへ登録する組み合わせがよく使われます。
音楽再生や位置情報の追跡のように、ユーザーが認識したうえで継続してほしい処理はフォアグラウンドサービスの領分です。ただしAndroid 14でサービス種別の宣言が強制され、Android 15以降はdataSync型に合計6時間の上限が入りました。「バックグラウンドで長時間動かしたいから常駐サービスにする」という発想は、通らなくなってきています。
端末側ジョブを見送りサーバー側キューへ移してよい処理の条件と判断軸
そもそも端末で実行する必然性を疑う視点も持っておきたいところです。集計、変換、外部APIへの転送。この種の処理は、端末の電池と回線と省電力仕様に左右されるより、サーバー側で回したほうが確実に終わります。Cloud Tasksのリトライと課金を整理した解説のような非同期キューを使えば、実行保証もリトライも運用側で握れます。
端末側に残すべきなのは、端末にしかないデータを扱う処理だけだと考えると整理しやすくなります。カメラで撮った画像、センサーのログ、オフライン中にローカルへ溜めた入力。これらをサーバーへ届けるまでがWorkManagerの仕事で、届いた後の加工はサーバーの仕事です。この線を引かずに端末側へ処理を積むと、動かない端末が出るたびに再現できない不具合として跳ね返ってきます。
実装でつまずく失敗パターンと保守を外部へ任せる判断軸の定め方
15分未満の定期実行を狙って処理の連鎖が破綻する典型パターン
最も多い詰まり方は、短い周期の定期実行を無理に作ろうとする設計です。下限が15分だと分かった後、OneTimeWorkRequestに遅延を付けて自分自身を再登録する回避策へ流れる例をよく見ます。この方法は書けてしまいますが、Dozeとバケット制御で間隔が伸びるため、狙った周期にはなりません。
加えて、再登録が失敗した瞬間に連鎖が止まる弱さがあります。一度切れると次が二度と来ないため、障害の発見も遅れる。要件が5分周期の同期なら、サーバー側からのプッシュで起こす設計へ組み替えるか、そもそも画面を開いたときに同期する方式へ寄せる判断をおすすめします。
端末メーカー独自の省電力で処理が止まる事象の原因切り分けの手順
「特定メーカーの端末だけ同期されない」という報告は、この分野の定番です。標準のDozeに加えて、独自の電池管理でバックグラウンド実行を打ち切る実装を載せている端末があり、アプリを電池の管理対象から外してもらわないと動きません。
切り分けの手順は決めておくと早くなります。まずADBでジョブの登録状態を確認し、登録自体が消えているのか、登録は残っていて実行されていないのかを分ける。前者ならアプリ側の再登録漏れかメーカー独自の打ち切り、後者なら制約の未充足かバケットの影響です。この2つを混ぜたまま調べると、原因にたどり着けません。
adb shell dumpsys jobscheduler | grep -A5 パッケージ名
adb shell dumpsys deviceidle get deep
バックグラウンド処理を外部へ任せる際に定める受け入れ基準の項目
バックグラウンド処理は、コードを読むだけでは正しさを判定しにくい領域です。実行されないという不具合は再現に時間がかかり、検証端末では出ないことも珍しくありません。だから外部へ委託する場合は、受け入れ基準を契約時に文章で決めておく必要があります。
基準は3つに絞れます。制約と周期の設計根拠が文書化されていること、リトライする失敗としない失敗の分類が定義されていること、そしてWorkManagerのテスト用実装を使った検証が成果物に含まれていること。Androidアプリ開発の受託では既存アプリの同期処理の棚卸しから対応しており、省電力仕様の変更に伴う実行漏れの調査や、サーバー側キューへの移し替えも含めて相談できます。
よくある質問
WorkManagerの導入でよく挙がる質問を、実装と発注の両面から5つ整理します。
WorkManagerで指定できる最短の繰り返し間隔は?
PeriodicWorkRequestの繰り返し間隔は15分が下限です。これより短い値を渡しても15分へ丸められます。さらに実行タイミングは制約と省電力の状態に左右されるため、15分ちょうどで走る保証もありません。短い周期が要件になる場合は、サーバー側からのプッシュや画面起動時の同期へ設計を寄せてください。
WorkerとCoroutineWorkerはどちらを使うべきですか?
Kotlinで新しく書くならCoroutineWorkerを選びます。中断関数をそのまま呼べるうえ、WorkManagerからの停止要求がコルーチンのキャンセルとして伝わるためです。Workerを使う場面は、Javaで書かれた既存コードへ組み込む場合や、単純な同期処理で依存を増やしたくない場合に限られます。
再起動してもタスクは残りますか?
再起動後もタスクは残る仕様です。登録内容は端末内のデータベースへ保存され、起動完了を受けて未完了のタスクが復元されます。ただし、アプリのデータを消去した場合や、ユーザーがアプリを強制停止した直後は挙動が変わります。強制停止後は次にアプリが起動されるまで復帰しない点を、仕様として説明できるようにしておいてください。
WorkManagerのテストはどう書きますか?
work-testingのWorkManagerTestInitHelperで初期化し、TestDriverで制約の充足や遅延の経過を手動で起こします。実時間を待たずに実行条件を満たせるため、15分周期の処理も即座に検証できます。2.11.0では停止理由を指定して実行中の処理を止めるメソッドが追加され、中断時の挙動も再現しやすくなりました。
導入に必要な最小のAPIレベルは?
2.11.0からminSdkがAPI 23(Android 6.0)へ引き上げられました。それ以前の版はAPI 21から使えるため、古い端末を対象に含むアプリでは版を上げる前に対応範囲を確認してください。2026年8月時点の安定版は2.11.2、次期系列は2.12.0-beta01がベータ段階にあります。
関連記事
- Jetpack Composeとは?宣言的UIの仕組みとBOM運用・採用判断を解説:進捗表示を組むUI層の前提です。
- Kotlinとは?Javaとの違いとK2コンパイラ・2.4系の現在地:Worker実装の言語側の土台になります。
- Androidアプリ開発の言語はKotlinとJavaのどちらか?選定基準と環境:既存資産との兼ね合いを判断できます。
- 非同期処理とは?同期処理との違いから実装方式まで解説:スケジューリングとの違いを整理できます。
- Androidアプリ開発とは?できること・開発の流れ・iOSとの違い:発注側の全体像を補います。