Battery Optimizationとの戦い 〜Dozeモードとメーカー独自最適化に泣かされた日々〜

※ChatGPTを使用して記事を作成しています。

Android 6.0 で導入された Dozeモード

さらに各メーカーが独自に組み込む「バッテリー最適化機能」。

これらはユーザーの電池持ちを守るための仕組みですが、アプリ開発者にとっては大きな壁でした。

通知が届かない、処理が止まる、スケジュールが遅れる――。

私は何度もこの罠に引っかかり、ユーザーからのクレームに震えました。

今回は「Battery Optimization」に振り回された実体験と、そこから得た教訓を語ります。

事件の始まり:通知が届かない

ある日、運営していたアプリにユーザーから問い合わせが殺到しました。

  • 「プッシュ通知が夜になると届かない」
  • 「朝になったらまとめて通知が来る」
  • 「アプリを開くまで何も更新されない」

サーバー側のログを見ると、通知は確かに送信済み。

にもかかわらず、端末側で受信されていない。

原因を調べた結果、Dozeモードが悪さをしていたのです。

Dozeモードの衝撃

Android 6.0 (Marshmallow) で追加されたDozeモードは、端末がアイドル状態になるとバックグラウンド処理を強制的に制限します。

  • ネットワークアクセス停止
  • JobSchedulerやAlarmManagerの実行延期
  • WakeLockの無効化

つまり「ユーザーが端末を放置している間は、ほとんど動けない」という仕様です。

私が実装していた「数時間おきにデータを同期して通知を出す仕組み」は、これによって完全に封じられていました。

苦肉の策:Whitelistへの追加依頼

当時の解決策としてよく挙がっていたのが、バッテリー最適化の無効化をユーザーにお願いする方法です。

if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
    val pm = context.getSystemService(Context.POWER_SERVICE) as PowerManager
    if (!pm.isIgnoringBatteryOptimizations(context.packageName)) {
        val intent = Intent(Settings.ACTION_REQUEST_IGNORE_BATTERY_OPTIMIZATIONS)
            .setData(Uri.parse("package:" + context.packageName))
        startActivity(intent)
    }
}

アプリ起動時にこのダイアログを出し、ユーザーに許可してもらう。

しかし、現実はそう甘くありませんでした。

  • ユーザーから「なんでそんな権限を求めるんだ」と不信感
  • 「怪しいアプリっぽい」とレビューで叩かれる
  • 大半のユーザーは設定を許可してくれない

結果、問題の根本解決にはなりませんでした。

さらなる地獄:メーカー独自のバッテリー最適化

Dozeモードの存在を理解し始めた矢先、さらに厄介な敵が現れました。

それが 中華系メーカーの独自バッテリー最適化 です。

  • Huaweiの「電池消費を制限」
  • Xiaomiの「自動起動制御」
  • OppoやVivoの aggressive kill

これらはOS標準のDozeよりも遥かに強力で、アプリが勝手にキルされる現象を引き起こしました。

テスト端末で試すと――

  • アプリを閉じると数分でバックグラウンドが完全終了
  • FCMのプッシュ通知も届かない
  • WorkManagerのジョブもキャンセル

もはや「Android」という統一基盤が意味をなさないほどのカオス。

失敗談:リリースして炎上

私はこれらの事情を知らず、普通にリリースしてしまいました。

結果どうなったか?

  • 「通知が来ない」と★1レビュー連発
  • 特にHuawei端末ユーザーからのクレームが爆発
  • 「他のアプリはちゃんと通知くるのに!」と怒られる

心が折れそうになりました。

対策を模索する日々

私は必死に調べ、次のような対策を試しました。

  1. 公式ホワイトリストへの追加依頼
    → ユーザーが拒否して終了。
  2. Foreground Serviceで動かし続ける
    → 常時通知が邪魔だと苦情殺到。
  3. FCM (Firebase Cloud Messaging) に全面移行
    → サーバー側でプッシュ制御することで大部分は解決。
  4. ユーザーへの案内を徹底
    → 設定画面に「バッテリー最適化を解除する方法」を端末ごとに掲載。
val intent = Intent(Settings.ACTION_IGNORE_BATTERY_OPTIMIZATION_SETTINGS)
startActivity(intent)

ただし、メーカーごとに画面が違いすぎて全対応は不可能でした。

最終的に学んだこと

1. 端末ごとの差異は避けられない

  • Dozeモード+メーカー独自仕様の組み合わせで、挙動は完全にバラバラ。
  • 「どの端末でも同じように動く」は幻想。

2. ユーザーの協力が必須

  • バッテリー最適化を解除してもらうか
  • プッシュ通知の遅延を理解してもらうか
  • UX設計の一部として「制約を前提」に作るしかない。

3. サーバー側でできることはサーバーに寄せる

  • 定期同期はやめて、可能な限り サーバーからのプッシュ通知 に変更。
  • アプリは「呼ばれたときに処理する」設計にシフト。

4. 「完璧な解決」は存在しない

  • 100%確実にバックグラウンドで動く方法はもう無い。
  • 開発者ができるのは「ベストエフォート」で最適化すること。

結果:設計の根本を変えるしかなかった

最終的にそのアプリでは、以下のように設計を変えました。

  • バックグラウンド定期処理 → 廃止
  • サーバーで新しいデータを検出したらFCM通知
  • アプリは通知をトリガーにして処理開始
  • ユーザーに「省電力設定解除」の案内を丁寧に表示

これにより、大多数のユーザー環境で「通知が届かない」問題は大きく減少しました。

教訓

  • Dozeモードは避けられない現実
  • メーカー独自最適化は予測不能
  • ユーザーに頼る部分を設計に組み込むべき

昔のように「勝手に裏で動かす」時代はもう終わりました。

これを受け入れ、ユーザー体験を壊さない範囲でどう設計するか が勝負です。

タイトルとURLをコピーしました