停止した部分的な wake lock

部分的な wake lock は PowerManager API のメカニズムであり、デベロッパーはこれを使用して、デバイスのディスプレイがオフになった後も(原因がシステム タイムアウトであってもユーザーが電源ボタンを押したことであっても)CPU が動作し続けるようにすることができます。アプリで部分的な wake lock を取得するには、PARTIAL_WAKE_LOCK フラグを指定して acquire() を呼び出します。アプリがバックグラウンドで実行されているときに部分的な wake lock が長時間保持されると、部分的な wake lock は「停止」します(アプリのどの部分もユーザーに表示されなくなります)。この状態ではデバイスが省電力状態にならないため、デバイスの電池が消耗します。部分的な wake lock は必要なときにのみ使用し、不要になったらすぐに解放する必要があります。

アプリで部分的な wake lock が停止している場合、このページのガイダンスが問題の診断と解決に役立ちます。

問題を検出する

アプリの部分的な wake lock が停止したことを常に把握できるとは限りません。アプリをすでに公開している場合は、Android Vitals が問題の発見に役立ちます。

Android Vitals

Android Vitals を使用すると、アプリで部分的な wake lock が停止しているときに、Google Play Console を介してアラートを受け取ることができます。これは、アプリのパフォーマンスを改善するために役立ちます。Android Vitals は、バックグラウンドでアプリが実行されているときに、バッテリー セッションで 1 時間に及ぶ部分的な wake lock が少なくとも 1 回発生すると、部分的な wake lock が停止したと報告します。

バッテリー セッションの定義は、プラットフォームのバージョンによって異なります。

  • Android 10 のバッテリー セッションは、指定された 24 時間以内に受信したすべてのバッテリー レポートが集約されたものです。バッテリー レポートは、2 回の充電(バッテリー残量が 20% 未満から 80% 超まで、または任意の充電レベルから 100% まで)の間隔を指します。
  • Android 11 のバッテリー セッションは、固定の 24 時間です。

表示されるバッテリー セッション数は、アプリの測定対象ユーザー全員の集計値です。Google Play が Android Vitals のデータを収集する方法については、Play Console のドキュメントをご覧ください。

アプリが部分的な wake lock を過度に停止していることがわかったら、問題に対処することが次のステップになります。

問題を解決する

wake lock は Android プラットフォームの初期のバージョンで導入されましたが、時間の経過とともに、以前は wake lock が必要だったユースケースでも、WorkManager のような新しい機能を使用する方が適切になっています。

このセクションでは wake lock を修正するためのヒントを紹介しますが、長期的には、おすすめの方法セクションの推奨事項に沿ってアプリを移行することを検討してください。

コード内で wake lock を取得する場所(newWakeLock(int, String) サブクラスや WakefulBroadcastReceiver サブクラスの呼び出しなど)を特定して修正します。次のヒントを参考にしてください。

  • wake lock が作成されたソース内の場所を特定しやすくするために、wake lock のタグ名にパッケージ名、クラス名、またはメソッド名を含めることをおすすめします。その他のヒントは次のとおりです。
    • 名前にメールアドレスなどの個人を特定できる情報(PII)を含めないようにします。そうしないと、デバイスは wake lock 名ではなく _UNKNOWN をログに記録します。
    • クラス名またはメソッド名をプログラムで(getName() の呼び出しなどで)取得しないでください。プログラムで取得すると Proguard によって難読化される可能性があるためです。代わりに、ハードコードされた文字列を使用します。
    • wake lock タグにカウンタまたは一意の識別子を追加しないでください。同じメソッドで作成された wake lock は、すべて一意の識別子を持つため、システムで集計できません。
  • コードで取得したすべての wake lock をコードで解放していることを確認してください。これは、acquire() の呼び出しすべてに release() の呼び出しが対応していることを確認するだけでは不十分です。捕捉されない例外が原因で wake lock が解放されない例を次に示します。

    Kotlin

    @Throws(MyException::class)
    fun doSomethingAndRelease() {
        wakeLock.apply {
            acquire()
            doSomethingThatThrows()
            release()  // does not run if an exception is thrown
        }
    }

    Java

        void doSomethingAndRelease() throws MyException {
            wakeLock.acquire();
            doSomethingThatThrows();
            wakeLock.release();  // does not run if an exception is thrown
        }

    正しいコードは次のとおりです。

    Kotlin

    @Throws(MyException::class)
    fun doSomethingAndRelease() {
        wakeLock.apply {
            try {
                acquire()
                doSomethingThatThrows()
            } finally {
                release()
            }
        }
    }

    Java

        void doSomethingAndRelease() throws MyException {
            try {
                wakeLock.acquire();
                doSomethingThatThrows();
            } finally {
                wakeLock.release();
            }
        }
  • wake lock は不要になったらすぐに解放するようにしてください。たとえば、バックグラウンド タスクの終了を許可するために wake lock を使用している場合は、タスクが終了したときに解放されるようにします。wake lock が解放されずに予想よりも長く保持された場合、バックグラウンド タスクに予想よりも時間がかかっている可能性があります。

コードの問題を解決したら、次の Android ツールを使用して、アプリが wake lock を正しく解放することを確認します。

  • dumpsys - デバイス上でシステム サービスのステータスに関する情報を提供するツール。wake lock のリストを含む電源サービスのステータスを確認するには、adb shell dumpsys power を実行します。

  • Battery Historian - Android バグレポートの出力を解析して、電源関連のイベントを視覚的に表示するツール。

ベスト プラクティス

一般的に、アプリは電池を消耗しやすいため、部分的な wake lock は避ける必要があります。Android には、以前は部分的な wake lock が必要だったほぼすべてのユースケースについて代替 API が用意されています。部分的な wake lock の残りのユースケースの 1 つは、画面がオフになっても音楽アプリの再生を続けるようにすることです。wake lock を使用してタスクを実行する場合は、バックグラウンド処理ガイドに記載されている代替の方法を検討してください。

部分的な wake lock を使用する必要がある場合は、次の推奨事項に従ってください。

  • アプリの一部がフォアグラウンドに残っていることを確認します。たとえば、サービスを実行する必要がある場合は、フォアグラウンド サービスを開始します。これにより、アプリがまだ実行中であることが視覚的に示されます。
  • wake lock の取得と解放のロジックが可能な限り単純であることを確認します。wake lock のロジックが、複雑なステートマシン、タイムアウト、エグゼキュータ プール、コールバック イベントに関連付けられている場合、そのロジックにわずかでもバグがあると、wake lock が予想よりも長く保持される可能性があります。そのようなバグは、診断とデバッグが困難です。