Hängende teilweise Wakelocks

Teil-Wakelocks sind ein Mechanismus in der PowerManager API, mit dem Entwickler die CPU am Laufen halten können, nachdem sich der Bildschirm eines Geräts ausschaltet (z. B. aufgrund einer Zeitüberschreitung des Systems oder durch Drücken der Ein/Aus-Taste). Ihre App erhält einen teilweisen Wakelock, indem sie acquire() mit dem Flag PARTIAL_WAKE_LOCK aufruft. Ein teilweiser Wakelock hänget fest, wenn er für längere Zeit angehalten wird, während die App im Hintergrund ausgeführt wird. Für den Nutzer ist kein Teil der App sichtbar. Diese Bedingung entlädt den Akku des Geräts, da dies verhindert, dass das Gerät in einen niedrigeren Stromzustand wechselt. Teil-Wakelocks sollten nur bei Bedarf verwendet und so schnell wie nicht mehr benötigt werden.

Wenn Ihre App einen teilweisen Wakelock hat, können Sie mithilfe der Anleitung auf dieser Seite das Problem diagnostizieren und beheben.

Problem erkennen

Vielleicht wissen Sie nicht immer, dass die Teil-Wakelocks Ihrer App hängen geblieben sind. Wenn du deine App bereits veröffentlicht hast, kann Android Vitals auf das Problem hinweisen.

Android Vitals

Mit Android Vitals kannst du die Leistung deiner App verbessern. Du wirst über die Play Console benachrichtigt, wenn in deiner App hängende Teil-Wakelocks auftreten. Android Vitals meldet, dass Teil-Wakelocks nach mindestens einer Stunde hängen bleiben, während Teil-Wakelocks im Hintergrund während einer Akkusitzung auftreten.

Die Definition der Akkusitzung hängt von der Plattformversion ab.

  • In Android 10 ist eine Akkusitzung die Aggregation aller Akkuberichte, die innerhalb eines bestimmten Zeitraums von 24 Stunden empfangen wurden. Ein Akkubericht bezieht sich auf das Intervall zwischen zwei Akkuladungen: entweder von unter 20% bis über 80% oder von jedem Akkustand bis 100%.
  • In Android 11 umfasst eine Akkusitzung einen festen Zeitraum von 24 Stunden.

Die angezeigte Anzahl der Akkusitzungen ist eine Summe für alle gemessenen Nutzer der App. Informationen dazu, wie Google Play Android Vitals-Daten erfasst, finden Sie in der Play Console-Dokumentation.

Sobald Sie sich bewusst sind, dass Ihre App übermäßig viele hängende Teil-Wakelocks aufweist, sollten Sie das Problem als Nächstes beheben.

Problem beheben

Wakelocks wurden in frühen Versionen der Android-Plattform eingeführt. Im Laufe der Zeit wurden jedoch viele Anwendungsfälle, für die zuvor Wakelocks erforderlich waren, durch neuere APIs wie WorkManager besser bedient.

Dieser Abschnitt enthält Tipps zum Beheben von Wakelocks. Langfristig sollten Sie Ihre App migrieren, um den Empfehlungen im Abschnitt Best Practices zu folgen.

Identifizieren und korrigieren Sie Stellen in Ihrem Code, an denen ein Wakelock ausgelöst wird, z. B. Aufrufe von newWakeLock(int, String) oder WakefulBroadcastReceiver-Unterklassen. Hier einige Tipps:

  • Wir empfehlen, den Namen des Pakets, der Klasse oder der Methode in den Namen des Wakelock-Tags aufzunehmen, damit Sie den Ort in der Quelle, an dem der Wakelock erstellt wurde, leicht identifizieren können. Hier einige weitere Tipps:
    • Lassen Sie alle personenidentifizierbaren Informationen wie E-Mail-Adressen im Namen weg. Andernfalls protokolliert das Gerät _UNKNOWN anstelle des Wakelock-Namens.
    • Rufen Sie den Klassen- oder Methodennamen nicht programmatisch ab, z. B. durch den Aufruf von getName(), da er von Proguard verschleiert werden könnte. Verwenden Sie stattdessen einen hartcodierten String.
    • Fügen Sie Wakelock-Tags keinen Zähler und keine eindeutigen IDs hinzu. Das System kann Wakelocks, die mit derselben Methode erstellt wurden, nicht aggregieren, da sie alle eindeutige Kennungen haben.
  • Achten Sie darauf, dass Ihr Code alle erzeugten Wakelocks freigibt. Das ist komplizierter, als dafür zu sorgen, dass für jeden Aufruf von acquire() ein entsprechender Aufruf von release() vorhanden ist. Hier ein Beispiel für einen Wakelock, der aufgrund einer nicht abgefangenen Ausnahme nicht freigegeben wird:

    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
        }

    Hier ist eine korrekte Version des Codes:

    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();
            }
        }
  • Achten Sie darauf, dass Wakelocks freigegeben werden, sobald sie nicht mehr benötigt werden. Wenn Sie beispielsweise einen Wakelock verwenden, damit eine Hintergrundaufgabe abgeschlossen werden kann, achten Sie darauf, dass die Freigabe erfolgt, wenn diese Aufgabe beendet ist. Wenn ein Wakelock länger als erwartet gehalten wird, ohne dass er freigegeben wird, kann dies bedeuten, dass Ihre Hintergrundaufgabe länger als erwartet dauert.

Nachdem Sie das Problem im Code behoben haben, prüfen Sie mit den folgenden Android-Tools, ob Ihre App Wakelocks korrekt freigibt:

  • dumpsys: Ein Tool, das Informationen zum Status von Systemdiensten auf einem Gerät bereitstellt. Führen Sie adb shell dumpsys power aus, um den Status des Energiedienstes, der eine Liste von Wakelocks enthält, anzusehen.

  • Akkuhistorian: Ein Tool, das die Ausgabe eines Android-Fehlerberichts in eine visuelle Darstellung von energiebezogenen Ereignissen parst.

Best Practices

Im Allgemeinen sollte Ihre App Teil-Wakelocks vermeiden, da sich der Akku des Nutzers zu einfach entleert. Android bietet alternative APIs für fast alle Anwendungsfälle, für die zuvor ein Teil-Wakelock erforderlich war. Ein verbleibender Anwendungsfall für Teil-Wakelocks besteht darin, dafür zu sorgen, dass eine Musik-App auch bei ausgeschaltetem Bildschirm weiter abgespielt wird. Wenn Sie Wakelocks zum Ausführen von Aufgaben verwenden, ziehen Sie die im Leitfaden zur Hintergrundverarbeitung beschriebenen Alternativen in Betracht.

Wenn Sie Teil-Wakelocks verwenden müssen, folgen Sie diesen Empfehlungen:

  • Achte darauf, dass ein Teil deiner App im Vordergrund bleibt. Wenn Sie beispielsweise einen Dienst ausführen müssen, starten Sie stattdessen einen Dienst im Vordergrund. Dies zeigt dem Nutzer visuell an, dass Ihre App noch ausgeführt wird.
  • Die Logik zum Erfassen und Lösen von Wakelocks sollte so einfach wie möglich sein. Wenn Ihre Wakelock-Logik an komplexe Statusmaschinen, Zeitüberschreitungen, Executor-Pools und/oder Callback-Ereignisse gebunden ist, kann jeder kleine Fehler in dieser Logik dazu führen, dass der Wakelock länger als erwartet gehalten wird. Diese Fehler sind schwer zu diagnostizieren und zu beheben.