Wake locks parciais travados

Wake locks parciais são um mecanismo da API PowerManager que possibilita que os desenvolvedores mantenham a CPU em funcionamento após a tela de um dispositivo ser desativada, seja devido ao tempo limite do sistema ou porque o usuário pressionou o botão liga/desliga. Seu app recebe um wake lock parcial ao chamar acquire() com a flag PARTIAL_WAKE_LOCK. Um wake lock parcial fica travado se for retido por muito tempo enquanto o app está sendo executado em segundo plano. Ou seja, quando nenhuma parte do app está visível para o usuário. Essa condição consome a bateria do dispositivo porque impede que ele entre em estados de baixo consumo de energia. Os wake locks parciais só devem ser usados quando for preciso e liberados assim que não forem mais necessários.

Caso seu app tenha um wake lock parcial travado, você pode usar as orientações nesta página para diagnosticar e corrigir o problema.

Detectar o problema

Nem sempre você sabe que os wake locks parciais do seu app estão travados. Se você já publicou o app, o recurso Android vitals pode ajudar a identificar o problema.

Android vitals

O recurso Android vitals pode ajudar a melhorar a performance do app alertando você, pelo Play Console, quando o app apresentar wake locks parciais travados. O Android vitals informa que há wake locks parciais travados quando ocorre pelo menos um wake lock parcial com duração de uma hora em segundo plano, em uma sessão de bateria.

A definição de duração da bateria depende da versão da plataforma.

  • No Android 10, a duração da bateria é a agregação de todos os relatórios de bateria recebidos em um determinado período de 24 horas. Um relatório de bateria se refere ao intervalo entre duas cargas, seja de menos de 20% a mais de 80% ou de qualquer nível de carga até 100%.
  • No Android 11, a duração da bateria é de um período fixo de 24 horas.

A duração da bateria mostrada é uma estimativa com base em informações de todos os usuários medidos do app. Parar informações sobre como o Google Play coleta dados do Android vitals, consulte a documentação do Play Console.

Depois de identificar wake locks parciais travados em excesso no app, a próxima etapa é resolver o problema.

Corrigir o problema

Os wake locks foram introduzidos nas primeiras versões da plataforma Android, mas, com o passar do tempo, muitos casos de uso que antes exigiam wake locks agora são mais bem atendidos por APIs mais recentes, como a WorkManager.

Esta seção contém dicas para corrigir wake locks, mas, a longo prazo, considere a possibilidade de migrar seu app para seguir as recomendações da seção de práticas recomendadas.

Identifique e corrija lugares no seu código em que há um wake lock, como chamadas para as subclasses newWakeLock(int, String) ou WakefulBroadcastReceiver. Veja algumas dicas:

  • Recomendamos que você inclua o nome do pacote, da classe ou do método no nome da tag do wake lock para identificar facilmente o local no código-fonte em que o wake lock foi criado. Confira mais algumas dicas:
    • Não inclua informações de identificação pessoal (PII, na sigla em inglês) no nome, como um endereço de e-mail. Caso contrário, o dispositivo vai registrar _UNKNOWN, em vez do nome do wake lock.
    • Não busque o nome da classe ou do método de forma programática, por exemplo, chamando getName(), porque o nome pode ser ofuscado pelo Proguard. Em vez disso, use uma string codificada.
    • Não adicione um contador ou identificadores exclusivos às tags de wake lock. O sistema não vai poder agregar wake locks criados pelo mesmo método, porque todos têm identificadores exclusivos.
  • Verifique se o código libera todos os wake locks que adquire. Isso é mais complicado do que garantir que todas as chamadas para acquire() tenham uma chamada correspondente para release(). Confira um exemplo de wake lock que não foi liberado devido a uma exceção não capturada:

    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
        }

    Veja a seguir uma versão correta do 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();
            }
        }
  • Não esqueça de liberar os wake locks assim que não forem mais necessários. Por exemplo, se você usa um wake lock para permitir que uma tarefa em segundo plano seja concluída, verifique se a liberação aconteça quando a tarefa é concluída. Se um wake lock for mantido por mais tempo do que o esperado sem ser liberado, isso pode significar que a tarefa em segundo plano está levando mais tempo do que o esperado.

Depois de corrigir o problema no código, verifique se o app libera os wake locks corretamente usando as ferramentas do Android abaixo:

  • dumpsys: uma ferramenta que fornece informações sobre o status dos serviços do sistema em um dispositivo. Para verificar o status do serviço de energia, que inclui uma lista de wake locks, execute adb shell dumpsys power.

  • Battery Historian: uma ferramenta que analisa a saída de um relatório de bugs do Android em uma representação visual de eventos relacionados à energia.

Práticas recomendadas

Em geral, seu app precisa evitar wake locks parciais, porque é muito provável que eles consumam a bateria do usuário rapidamente. O Android fornece APIs alternativas para quase todos os casos de uso que antes precisavam de um wake lock parcial. Um caso de uso restante para wake locks parciais é garantir que um app de música continue sendo executado quando a tela estiver desligada. Se você estiver usando wake locks para executar tarefas, considere as alternativas descritas no guia de processamento em segundo plano.

Se você precisar usar wake locks parciais, siga estas recomendações:

  • Não se esqueça de manter parte do seu app em primeiro plano. Por exemplo, se você precisar executar um serviço, inicie-o em primeiro plano. Isso indica visualmente para o usuário que seu app ainda está em execução.
  • Confira se a lógica para adquirir e liberar wake locks é o mais simples possível. Quando sua lógica de wake lock está vinculada a máquinas de estado, tempos limite, pool de executores e/ou eventos de callback complexos, qualquer bug sutil nessa lógica pode fazer com que o wake lock seja mantido por mais tempo do que o esperado. Esses bugs são difíceis de diagnosticar e depurar.