Bloqueos de activación parciales sostenidos

Los bloqueos de activación parciales son un mecanismo en la API de PowerManager que les permite a los desarrolladores mantener la CPU en ejecución después de que se apaga la pantalla de un dispositivo (ya sea debido al tiempo de espera del sistema o a que el usuario haya presionado el botón de encendido). Para adquirir un bloqueo de activación parcial, tu app debe llamar a acquire() con la marca PARTIAL_WAKE_LOCK. Se vuelve sostenido cuando se mantiene durante un tiempo prolongado mientras tu app se ejecuta en segundo plano (el usuario no puede ver ninguna parte). Esta condición agota la batería del dispositivo, ya que impide que ingrese en el estado de bajo consumo. Debes usar el bloqueo de activación parcial solo cuando sea necesario y liberarlo cuando ya no lo necesites.

Si tu app presenta un bloqueo de activación parcial sostenido, puedes seguir las indicaciones que se incluyen en esta página para diagnosticar el problema y corregirlo.

Cómo detectar el problema

Quizás no siempre estés al tanto de que los bloqueos de activación parciales de tu app son sostenidos. Si ya la publicaste, Android vitals puede ayudarte a que te enteres del problema.

Android vitals

Android vitals puede ayudarte a mejorar el rendimiento de tu app. Para ello, te envía alertas a través de Play Console cuando tu app presenta bloqueos de activación parciales sostenidos. Android vitals los registra como sostenidos cuando se produce, como mínimo, un bloqueo de activación parcial de una hora de duración en uno de los siguientes:

  • Al menos el 0.70% de las sesiones de batería en su totalidad.
  • Al menos el 0.10% de las sesiones de batería mientras se ejecuta solo en segundo plano.

Una sesión de batería es el intervalo entre dos cargas de batería completa. La cantidad de sesiones de batería que se muestra representa un compilado de todos los usuarios medidos de la app. Para obtener información sobre cómo Google Play recopila datos de Android vitals, consulta la documentación de Play Console.

Una vez que se te informa que tu app presenta demasiados bloqueos de activación parciales sostenidos, el siguiente paso es abordar el problema.

Cómo corregir el problema

Los bloqueos de activación se introdujeron en las primeras versiones de la plataforma de Android. Sin embargo, con el tiempo, muchos casos prácticos que antes requerían bloqueos de activación ahora se llevan a cabo mejor con API nuevas, como WorkManager.

En esta sección, podrás encontrar sugerencias para corregir los bloqueos de activación. No obstante, a largo plazo, analiza la posibilidad de migrar tu app de acuerdo con las recomendaciones incluidas en la sección de prácticas recomendadas.

Identifica y corrige las partes de tu código que presenten un bloqueo de activación, como newWakeLock(int, String) o WakefulBroadcastReceiver. A continuación, se incluyen algunas sugerencias:

  • Incluye el nombre de tu paquete, clase o método en el nombre de la etiqueta de bloqueo de activación para que puedas identificar con facilidad la ubicación en el origen donde se creó. Aquí, encontrarás algunas sugerencias adicionales:

    • No incluyas información de identificación personal (PII) en el nombre, como una dirección de correo electrónico. De lo contrario, el dispositivo registrará _UNKNOWN, en lugar del nombre del bloqueo de activación.
    • No obtengas el nombre de la clase o del método de forma programática, por ejemplo, al llamar a getName(), ya que Proguard podría ofuscarlo. En su lugar, usa una string hard-coded.
    • No agregues un contador ni identificadores únicos a las etiquetas de bloqueo de activación. El sistema no podrá agregar bloqueos de activación creados por el mismo método, ya que todos tendrán identificadores únicos.
  • Asegúrate de que tu código libere todos los bloqueos de activación que adquiera. Esto es más complicado que asegurarse de que cada llamada a acquire() tenga una llamada correspondiente a release(). A continuación, podrás ver un ejemplo de un bloqueo de activación que no se libera debido a una excepción inesperada:

    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
            }

    Esta es una versión correcta del código:

    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();
                }
            }
  • Asegúrate de liberar los bloqueos de activación cuando ya no se necesiten. Por ejemplo, si utilizas uno para permitir que finalice una tarea en segundo plano, asegúrate de que se libere cuando finalice. Si un bloqueo de activación se mantiene más de lo esperado sin liberarse, podría deberse a que tu tarea en segundo plano tarda más de lo esperado.

Una vez que hayas corregido el problema en el código, verifica que tu app libere los bloqueos de activación correctamente con las siguientes herramientas de Android:

  • dumpsys: Es una herramienta que proporciona información sobre el estado de los servicios del sistema en un dispositivo. Para ver el estado del servicio de batería, que incluye una lista de los bloqueos de activación, ejecuta el comando adb shell dumpsys power.

  • Battery Historian: Es una herramienta que analiza los resultados de un informe de errores de Android y genera una representación visual de los eventos relacionados con la batería.

Prácticas recomendadas

En general, tu app debe evitar los bloqueos de activación parciales, ya que agotan la batería del usuario. Android proporciona API alternativas para casi todos los casos prácticos que antes requerían un bloqueo de activación parcial. Un caso práctico restante para los bloqueos de activación parciales es garantizar que una app de música siga reproduciéndose cuando se apaga la pantalla. Si utilizas los bloqueos de activación para ejecutar tareas, analiza las alternativas que se describen en la guía de procesamiento en segundo plano.

Si debes usar bloqueos de activación parciales, sigue estas recomendaciones:

  • Asegúrate de que parte de tu app permanezca en primer plano. Por ejemplo, si debes ejecutar un servicio, inicia un servicio en primer plano en su lugar. De este modo, el usuario tendrá una indicación visual de que la app aún se está ejecutando.
  • Asegúrate de que la lógica para adquirir y liberar los bloqueos de activación sea lo más simple posible. Cuando esta lógica se vincula a máquinas de estado complejo, tiempos de espera, grupos de ejecutores o eventos de devolución de llamada, cualquier error en dicha lógica, por pequeño que sea, puede hacer que el bloqueo de activación se mantenga más de lo esperado. Estos errores son difíciles de diagnosticar y depurar.