Wake locks parciais travados

Wake locks parciais são um mecanismo da API PowerManager que permite aos desenvolvedores manter 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 adquire um wake lock parcial chamando acquire() com a sinalização PARTIAL_WAKE_LOCK. Um wake lock parcial fica travado se permanece retido por muito tempo enquanto seu 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.

Se seu app tiver um wake lock parcial travado, você poderá 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 você a ficar sabendo do problema.

Android vitals

O recurso "Android vitals" pode ajudar a melhorar o desempenho do app alertando você, por meio do Play Console, quando o app apresentar wake locks parciais travados. O "Android vitals" informa que os wake locks parciais estão travados quando ocorrer pelo menos um wake lock parcial, com duração de uma hora, em:

  • Pelo menos 0,70% da duração da bateria como um todo
  • ou

  • Pelo menos 0,10% da duração da bateria durante a execução apenas em segundo plano.

A duração da bateria se refere ao intervalo entre duas cargas completas da bateria. A duração da bateria exibida é um agregado de todos os usuários medidos no app. Para ver informações sobre como o Google Play coleta dados do "Android vitals", consulte a documentação do Play Console.

Depois que você souber que o app tem wake locks parciais travados em excesso, a próxima etapa será 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 seus 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 que adquirem um wake lock, como 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. Veja 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 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 ele 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 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 cada chamada para acquire() tenha uma chamada correspondente para release(). Veja 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();
                }
            }
  • Certifique-se de que os wake locks sejam liberados 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, certifique-se de que a liberação aconteça quando a tarefa for concluída. Se um wake lock é mantido por mais tempo do que o esperado sem ser liberado, isso pode significar que sua tarefa em segundo plano está levando mais tempo que o esperado.

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

  • dumpsys: uma ferramenta que fornece informações sobre o status dos serviços do sistema em um dispositivo. Para ver 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 a energia.

Práticas recomendadas

Em geral, seu app precisa evitar wake locks parciais, porque eles consomem com muita facilidade a bateria do usuário. 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 tocando quando a tela estiver desligada. Se você usa 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:

  • Certifique-se de que parte do seu app permaneça 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.
  • Certifique-se de que a lógica para adquirir e liberar wake locks seja 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.