Androidの通知が遅れるのはなぜ?Doze・App Standbyの違いと対処法

当ページのリンクには広告が含まれています。
Doze・App Standby・バッテリー最適化の文字と、通知カードやスマートフォンの設定画面を描いたイメージ

画面をロックしてしばらく経ってから開いたら通知がどっと届いていた。そんな経験ありますよね。逆に開発者側だと「WorkManagerで毎朝8時に処理するよう組んだはずなのに、気づいたら9時過ぎに実行されている」みたいな話もよく聞きます。

Androidには意図的にバックグラウンド処理を遅らせる仕組みがいくつかあります。Doze、App Standby、「バッテリーの最適化」という端末設定です。

この記事ではそれぞれの仕組みの違いとユーザーとして確認できる設定、開発者として気をつけるポイントを公式ドキュメントをもとに整理しました。

目次

通知やバックグラウンド処理が遅れる仕組み

Androidでは端末が使われていないときに働くDozeやアプリの利用状況に応じたApp Standbyの制限によって通知やバックグラウンド処理が遅れることがあります。バッテリーの最適化はこうした省電力制限の適用に関わる設定です。まずは仕組みと設定の違いを一覧で確認します。

仕組み制限の単位導入バージョン何をするか
Doze端末全体Android 6.0(APIレベル23)画面OFF+未使用が続くとネットワークやジョブをまとめて延期する
App StandbyアプリごとAndroid 6.0(APIレベル23)しばらく使っていないアプリのバックグラウンド処理を制限する
App Standby BucketアプリごとAndroid 9(RestrictedはAndroid 12で追加)利用状況などに応じて分類し、ジョブやアラームなどを制限する
バッテリーの最適化アプリごとの設定Android 6.0から除外設定に対応、画面や選択肢はOS・機種によるアプリへの省電力制限の適用を調整する

ユーザー側で確認・変更できる設定

通知が遅れるときは、まず対象アプリの通知が許可されているか確認します。Android 13以降では通知の許可が必要なため、許可されていない状態をバッテリー設定だけで直すことはできません。そりゃそうですよね。

通知は表示されているのに音だけ鳴らない場合はサイレント通知やサイレントモードの設定を確認します。画面を点けたときやアプリを開いたときにまとめて届く場合はバッテリー設定も確認してみてください。

  1. 「設定」アプリを開く。
  2. 「アプリ」から対象のアプリを選択する。
  3. 「バッテリー」または「バッテリーの使用量」を開く。
  4. バックグラウンドでの使用が制限されていないか確認する。「制限付き」になっている場合まず「最適化」へ変更する。

メーカーや機種、OSバージョンによってメニューの名称や階層は変わることがあります。「アプリの電池を最適化」のような表記になっている場合もあるので、見つからないときは設定内の検索機能を使うのがおすすめです。

「最適化」でも遅れる場合は必要なアプリだけ一旦「制限なし」に変更して様子を見てください。効果がないようであれば変更前の設定に戻してください。あわせて、端末全体のバッテリーセーバーやアプリ内の通知設定も確認します。

Dozeとは?端末全体にかかる省電力モード

Android Developersの公式ドキュメントによると、Dozeはデバイスが長期間使用されていないときにアプリのバックグラウンドCPUとネットワークアクティビティを遅延させる仕組みです。Android 6.0(APIレベル23)以降で動作するアプリ全般に影響するのでtargetSdkVersionを古いままにしていても対象になります。

Doze中は主に次のような制限がかかります。

  • ネットワークアクセスが一時停止する。
  • ウェイクロックが無視される。
  • 通常のAlarmManagerアラーム(setExact()やsetWindow())が次のメンテナンス時間枠まで延期される。
  • Wi-Fiスキャンが行われない。
  • アダプター同期やJobSchedulerのジョブが延期される(WorkManagerも内部でJobSchedulerを使うため同様に延期される)。

これらの制限は「メンテナンス時間枠」という短い時間だけ解除されます。この時間枠でアプリは保留していたジョブや同期をまとめて実行しまた次のスリープに入ります。放置される時間が長くなるほどこのスリープ間隔もどんどん延びていくので通知が思い出したようにまとめて届く体感につながっているわけです。

画面をOFFにしただけでも制限される「簡易Doze」

ここが見落とされがちなポイントなのですが、Dozeには実は2段階あります。AOSPのドキュメントによるとAndroid 7.0以降では端末が静止していなくても画面をOFFにしただけで「簡易Doze」という軽量版の最適化が入るようになりました。

深いDoze簡易Doze
発生条件画面OFF+バッテリー駆動+端末が静止画面OFF+バッテリー駆動(充電はしていない)
タイミングメンテナンスごとに間隔が徐々に延びるメンテナンス時間枠ごとに数分間を繰り返す
主な制限ネットワーク・ウェイクロック・GPS/Wi-Fiスキャンなし。アラームやジョブも延期ネットワークアクセスなし。メンテナンス時間枠以外はジョブ・同期を延期
通知の届き方優先度の高いプッシュ通知のみ通話やインスタントメッセージなどのリアルタイム通信は届く
終了条件動き・画面ON・充電開始・目覚まし用アラームの発火前画面ON

ポケットに入れて持ち歩いていても充電しておらず画面がOFFなら、簡易Dozeの影響を受けることがあります。なお公式ドキュメントには通知が届いただけではDozeモードは終了しないとも明記されています。画面をONにするなど別のアクションがあって初めてDozeが解除される点も覚えておくとよさそうです。

App Standby(アプリスタンバイ)とは?アプリごとの利用頻度による制限

Dozeが端末全体にかかる制限に対してApp Standbyは「このアプリ最近使われてないな」とシステムが判断したアプリに個別にかかる制限です。ユーザーがアプリを操作していない、フォアグラウンドで動いていない、通知も出していない、といった条件が続くとアイドル状態と判定されます。

Standby Bucket(優先度バケット)の5段階

Android 9(APIレベル28)でApp Standby Bucketが導入されAndroid 12(APIレベル31)で「制限付き(Restricted)」が追加されました。現在は主に5つのバケットでアプリが使えるジョブやアラームなどを制限します。インストール後に一度も起動していないアプリ向けには別にNeverバケットもあります。

次の数値は公式資料に掲載されている実行枠の目安です。アクティブの「60分で最大20分」はAndroid 16以降の説明に対応します。端末の充電状態やアプリの状態などでも扱いが変わるため、表の時間だけジョブの実行が確約されてるわけではないです。

バケット判定の目安通常ジョブの実行枠アラームネットワーク
アクティブ起動中・直近で操作した60分間で最大20分制限なし制限なし
ワーキングセットほぼ毎日使う4時間で最大10分1時間に10回まで制限なし
高頻度(Frequent)毎日ではないがよく使う12時間で最大10分1時間に2回まで制限なし
低頻度(Rare)あまり使わない24時間で最大10分1時間に1回まで無効
制限付き(Restricted)長期間放置・過剰な処理1日1回、最大10分1日1回のみ無効

このほかに「優先ジョブ(expedited job)」には別枠の上限があるなど、表には収まりきらない細かい規定もあります。正確な最新値は変わることがあるので、厳密な数値が必要な場合は公式ドキュメントも確認してください。

特に注意したいのが制限付きバケットです。除外対象でないアプリはAndroid 13以降ではユーザーによる操作が8日間ないとこのバケットへ移されます。Android 12・12Lでは45日間だったので、かなり厳しくなっていますね。端末の電源が切れている時間はこの日数に含まれません。バックグラウンドの位置情報権限を持つアプリや有効なウィジェットを設置しているアプリなどは除外対象になります。

画面をOFFにすると何が制限される?デバイスの状態別に比較

DozeとApp Standbyは同時に効いてくるので、実際の挙動はデバイスの状態によっても変わります。電源管理リソースの上限のページの内容を整理するとこうなります。

デバイスの状態ジョブアラームネットワークFCM
充電中制限付きバケット以外は上限なしほぼ制限なし制限なし制限なし
画面ON(バッテリー駆動)バケットに応じて上限ありバケット・プロセス状態に応じて上限ありバケットなどにより異なる制限なし
画面OFF・Doze動作中バケットに応じて上限あり、実行はメンテナンス時間枠まで延期通常アラームはメンテナンス時間枠まで延期。アイドル時アラームは1時間に7回までDoze中は原則として制限される高優先度は即時配信を試みる。通常優先度はメンテナンス時間枠などまで遅れることがある

充電中は多くの省電力制限が緩和されるため、バッテリー駆動中との比較で原因を調べる手がかりになります。通知の遅延は通信状態やアプリ側の処理も関わるため充電中なら必ず早く届くとは限りません。ややこしいですね。

WorkManagerは指定した時刻どおりに動く?

WorkManagerは実行条件を満たしたタイミングでバックグラウンド処理を行うための仕組みです。「毎朝8時ちょうど」のような実行時刻は保証しません。初回の待ち時間や繰り返し間隔を設定しても、省電力制限やネットワークなどの条件によって実行が遅れることがあります。

Android 16(APIレベル36)からはこのあたりの割り当てロジックが少し変わりました。「アプリがどのバケットに属しているか」「トップ状態でジョブが開始されたか」「フォアグラウンドサービス実行中か」といった条件をもとに実行枠が調整されるようになっています。それまではアクティブバケットやフォアグラウンドサービス実行中であれば実行に上限がなかったのですが、Android 16からはそこにも一定の枠が設けられるようになりました。「前のバージョンでは動いていたのに」という場合はこのあたりが原因になっていることもありそうです。

APIは処理の目的に合わせて選びます。ユーザーにとって重要な短い処理をできるだけ早く始めたい場合はWorkManagerのsetExpedited()が候補になりますが、実行枠やシステムの負荷によって開始が遅れることがあります。

Android 14以降のJobInfo.Builder.setUserInitiated(true)はユーザー操作で始めるファイルのダウンロードなど時間のかかるデータ転送向けです。指定時刻に処理を予約するためのものではありません。

目覚ましや予定のリマインダーなど、正確な時刻が必要な機能ではAlarmManagerの正確なアラームを検討します。Doze中にも発火させたい場合はsetExactAndAllowWhileIdle()などを使いますが、対象OS・targetSdkVersion・用途に応じた権限の確認が必要です。アラームをきっかけにWorkManagerへ処理を渡してもその処理の即時実行まで保証されるわけではありません。

FCMならDoze中でも通知は届く?

Firebase Cloud MessagingのドキュメントによるとFCMには「標準(normal)」と「高(high)」の2つの優先度があります。高優先度メッセージはFCMが即時配信を試み、必要に応じてスリープ状態のデバイスを一時的に起こしてくれます。標準優先度のメッセージはDozeモード中だと配信がメンテナンス時間枠まで持ち越されます。

ここで注意したいのが、高優先度メッセージは「ユーザーに通知を表示するためのもの」という前提がある点です。通知を伴わないバックグラウンド同期のためだけに高優先度を使い続けると、過去7日間の挙動をもとにFCM側が自動的に優先度を通常へ下げてしまうことがあります。こういった、いわばペナルティ的な動きがあるようで「high priorityにしておけば安心」という単純な話ではないんですよね。

Dozeへの対応としてAndroid Developersが挙げているポイントは次のとおりです。

  • ダウンストリームの通信にはできるだけFCMを使う。
  • ユーザーへの即時通知が必要な場合だけ高優先度メッセージにする。
  • 最初のメッセージペイロードに十分な情報を入れて後続の通信を減らす。
  • 重要なアラームはsetAndAllowWhileIdle()やsetExactAndAllowWhileIdle()で設定する。

バッテリーの最適化を「制限なし」にすれば解決する?

端末によってはアプリごとのバッテリー設定に「制限なし」「最適化」「制限付き」が用意されています。選択肢の名称や設定方法はメーカーやAndroidのバージョンによって異なります。

バッテリー設定設定内容
制限なしアプリに対するバッテリー使用の制限を緩めます。通知や処理の遅延が改善する場合がありますがバッテリー消費が増える可能性があります。
最適化ユーザーの操作パターンに応じてバックグラウンド処理を最適化します。多くの端末で標準になっている設定です。
制限付きバックグラウンドでの実行がほぼできなくなります。アプリが想定どおりに動かないことがあります。

バッテリー最適化の除外対象になったアプリはDoze中もネットワークにアクセスし、CPUのスリープを防ぐ部分的なウェイクロックを保持できます。通常のアラームなどは除外しても残る制約があります。ジョブの扱いもAndroidのバージョンによって異なり、Android 16以降はユーザーがバッテリー使用を「制限なし」にしたアプリにもジョブの実行枠があります。

開発者向けにはバッテリー最適化の設定一覧を開く方法と、自分のアプリを直接除外するよう求める方法が用意されています。直接の除外要求にはアプリの主要機能が省電力制限によって支障を受けるなどの条件があります。設定画面への案内と、直接の除外要求は区別して考える必要があります。

メーカー独自の制限にも注意が必要

ここまではAndroid標準の仕組みですが、実際には端末メーカーが独自にバックグラウンド制限を上乗せしているケースも珍しくありません。特に一部のメーカー製端末では、標準のDoze・App Standbyよりもさらに踏み込んだアプリ終了処理が行われることがあると報告されています。

具体的な制限内容はメーカーやモデル、OSバージョンによってかなり差があるため、この記事では断定的な数値は避けますが、自分の端末がどれくらいアプリを終了させやすいかは「DontKillMyApp」のようなベンチマークアプリで大まかに確認できます。「Android側の設定は合っているはずなのに通知が来ない」という場合は、メーカー独自の設定(バッテリーセーバーの別枠設定やアプリロック機能など)も見直してみるとよさそうです。

【開発者向け】adbコマンドでDoze・App Standbyを再現してテストする

自然にDozeやApp Standbyになるのを待っていると検証だけで日が暮れてしまいます。Android Developersの公式ドキュメントではadbコマンドで強制的に状態を切り替える方法が案内されています。

以下のコマンドはadbを利用できるPCのターミナルで実行します。Android端末のUSBデバッグまたはワイヤレスデバッグを有効にし、対象アプリをインストールしておいてください。


adbで端末に接続する手順や実行タイミングを確認するログの取り方は次の記事にまとめています。

Dozeモードを再現する

bash
adb shell dumpsys deviceidle force-idle

強制的にDozeのアイドル状態にします。確認が終わったら次のコマンドで解除します。

bash
adb shell dumpsys deviceidle unforce
adb shell dumpsys battery reset

App Standbyを再現する

bash
adb shell dumpsys battery unplug
adb shell am set-inactive 【パッケージ名】 true

復帰させる場合は以下のコマンドです。

bash
adb shell am set-inactive 【パッケージ名】 false
adb shell dumpsys battery reset

battery unplugはバッテリー駆動の状態を模擬するコマンドです。検証が終わったらbattery resetで実際のバッテリー状態へ戻します。

現在のStandby Bucketを確認する

bash
adb shell am get-standby-bucket 【パッケージ名】

このコマンドで対象アプリが今どのバケットに分類されているかを直接確認できます。

実際に画面ON、画面OFF、Doze強制、バッテリー最適化除外の4パターンでWorkManagerやFCMの実行タイミングをログ比較してみると制限の効き方がかなり体感できるはずです。

まとめ

  • Dozeは端末単位の省電力モード、App Standbyはアプリ単位の利用頻度による制限で仕組みそのものが別だよ。
  • 充電していないときは端末を持ち歩いていても画面OFFの状態で簡易Dozeの影響を受けることがあるよ。
  • 後から実行できる処理はWorkManager、即時性が必要なプッシュ通知は高優先度FCM、正確な時刻が必要な機能はAlarmManagerと目的に合わせて使い分けよう。
  • バッテリー最適化の除外で緩和される制限はあるけど、すべての処理が自由になるわけではないよ。必要なアプリだけ設定を変えて効果と電池の減り方を確認してみてね。
  • メーカー独自の制限が絡んでいそうな場合はDontKillMyAppのようなツールで自分の端末の挙動を確認してみてね。

Androidの設定やAndroid Studio・ADBを使った開発情報を探したい方は目的別のまとめも参考にしてください。

よかったらシェアしてね!
  • URLをコピーしました!
  • URLをコピーしました!
目次