Otimizar localização para poupar bateria

Os limites da localização em segundo plano introduzidos no Android 8.0 (nível 26 da API) trouxeram um novo foco com relação a como o uso dos Serviços de localização afeta o consumo de bateria. Esta página aborda algumas práticas recomendadas dos serviços de localização e o que pode ser feito agora para que o uso de bateria pelos seus apps seja mais eficiente. Adotar essas práticas traz vantagens ao app, independentemente da versão da plataforma em que ele opera.

Os limites da localização em segundo plano no Android 8.0 apresentaram as mudanças a seguir:

  • A coleta de localização em segundo plano é limitada e a localização é calculada e entregue apenas algumas vezes por hora.
  • As verificações de Wi-Fi são mais conservadoras e as atualizações de localização não são computadas quando o dispositivo permanece conectado ao mesmo ponto de acesso estático.
  • A responsividade da fronteira geográfica virtual muda de dezenas de segundos para aproximadamente dois minutos. Essa mudança melhora notavelmente o desempenho da bateria: até 10 vezes mais eficiência em alguns dispositivos.

Esta página parte do princípio de que você está usando as APIs dos Serviços de localização do Google, que oferecem maior precisão e impõem um gasto de bateria menor do que as APIs de localização de framework. Em particular, esta página supõe familiaridade com a API do provedor de localização misto, que combina sinais de GPS, Wi-Fi e redes de celular, além do acelerômetro, giroscópio, magnetômetro e outros sensores. Também é necessário conhecer a API da fronteira geográfica virtual, criada com base na API do provedor de localização misto e otimizada para o desempenho de bateria.

Entender o consumo de bateria

A coleta de localização e o consumo de bateria têm relação direta nos aspectos a seguir:

  • Precisão: a precisão dos dados de localização. Em geral, quanto melhor a precisão, maior será o consumo de bateria.
  • Frequência: a frequência com que a localização é calculada. Quanto maior a frequência do cálculo, mais a bateria será usada.
  • Latência: a velocidade de entrega dos dados de localização. Uma latência menor geralmente requer menos consumo de bateria.

Precisão

É possível especificar a precisão da localização com o método setPriority(), transmitindo um dos valores a seguir como argumento:

  • PRIORITY_HIGH_ACCURACY fornece a localização mais precisa possível, calculada usando quantas entradas forem necessárias (ativa o GPS, Wi-Fi e celular e usa diversos sensores) e pode causar um consumo significativo de bateria.
  • PRIORITY_BALANCED_POWER_ACCURACY fornece localização precisa com otimização de energia. É muito raro o uso do GPS nesse caso. Normalmente, é usada uma combinação de informações de Wi-Fi e celular para calcular a localização do dispositivo.
  • PRIORITY_LOW_POWER depende fortemente das torres de celular e evita as entradas de GPS e Wi-Fi, fornecendo uma precisão aproximada (no nível de cidade) com o mínimo consumo possível de bateria.
  • PRIORITY_NO_POWER recebe as localizações passivamente de outros apps para os quais a localização já foi calculada.

As necessidades de localização da maioria dos apps podem ser atendidas por meio de opções de energia baixa ou equilibrada. A alta precisão precisa ser reservada para apps que operam em primeiro plano e exigem atualizações de localização em tempo real (por exemplo, um app de mapa).

Frequência

Há dois métodos possíveis para especificar a frequência da localização:

  • Use o método setinterval() para especificar o intervalo em que a localização é calculada para seu app.
  • Use setFastestInterval() para especificar o intervalo em que a localização calculada para outros apps é entregue ao seu app.

Ao usar setInterval(), passe o maior valor possível. Isso é especialmente verdade com relação à coleta de localização em segundo plano, que costuma ser uma fonte de consumo indesejado de bateria. Usar intervalos de alguns segundos é algo a ser reservado para os casos de uso em primeiro plano. Os limites de localização em segundo plano introduzidos no Android 8.0 impõem essas estratégias, mas seu app precisa se esforçar para aplicá-las nos dispositivos Android 7.0 ou versões anteriores.

Latência

É possível especificar a latência com o método setMaxWaitTime(), normalmente transmitindo um valor muitas vezes maior do que o intervalo especificado no método setInterval(). Essa configuração atrasa a entrega da localização e muitas atualizações de locais podem ser entregues em lotes. As duas mudanças citadas ajudam a minimizar o consumo de bateria.

Caso o app não precise da atualização de localização imediatamente, transmita o maior valor possível ao método setMaxWaitTime(), efetivamente trocando a latência por mais dados e eficiência de bateria.

Ao usar fronteiras geográficas virtuais, os apps precisam transmitir um valor alto no método setNotificationResponsiveness() para economizar energia. É recomendado um valor de cinco minutos ou mais.

Casos de uso da localização

Esta seção descreve alguns cenários de coleta de localização típicos e recomendações para otimizar o uso das APIs do provedor de localização misto e da fronteira geográfica virtual.

Atualizações visíveis ao usuário ou em primeiro plano

Exemplo: um app de mapeamento que precisa de atualizações frequentes e precisas com latência muito baixa. Todas as atualizações acontecem em primeiro plano: o usuário inicia uma atividade, consome os dados de local e, após um curto período, interrompe a atividade.

Use o método setPriority() com um valor de PRIORITY_HIGH_ACCURACY ou PRIORITY_BALANCED_POWER_ACCURACY.

O intervalo especificado no método setInterval() depende do caso de uso: para cenários em tempo real, defina o valor de alguns segundos. Caso contrário, limite a alguns minutos, sendo que o recomendado para minimizar o uso da bateria é de aproximadamente dois minutos ou mais.

Receber a localização do dispositivo

Exemplo: um app de clima quer saber a localização do dispositivo.

Use o método getLastLocation(), que retorna a localização disponibilizada mais recentemente, que pode ser nula em casos raros. Esse método fornece uma maneira simples de acessar a localização sem incorrer em custos associados às solicitações de atualizações de local. Use em conjunto com o método isLocationAvailable(), que retorna true quando a localização retornada por getLastLocation() está razoavelmente atualizada.

Iniciar atualizações quando um usuário está em uma localização específica

Exemplo: solicitar atualizações quando um usuário estiver a uma determinada distância do trabalho, da casa ou de outro local.

Use a fronteira geográfica virtual em conjunto com as atualizações do provedor de localização combinada. Solicite as atualizações quando o app receber um acionador de entrada da fronteira geográfica virtual e as remova quando o app receber um acionador de saída desse tipo de fronteira. Isso garante que o app tenha atualizações de localização mais granulares somente quando o usuário tiver entrado em uma área definida.

O fluxo de trabalho típico para esse cenário pode envolver o surgimento de uma notificação na transição de entrada da fronteira geográfica virtual e o início de uma atividade que contém código para solicitar atualizações quando o usuário tocar na notificação.

Iniciar atualizações com base no estado da atividade do usuário

Exemplo: solicitar atualizações somente quando o usuário estiver dirigindo ou pedalando.

Use a API Activity Recognition em conjunto com as atualizações do provedor de localização combinada. Solicite as atualizações quando a atividade direcionada for detectada e remova as atualizações quando o usuário parar de executar a atividade.

O fluxo de trabalho típico para esse caso de uso pode envolver o surgimento de uma notificação para a atividade detectada e o início de uma atividade que contém código para solicitar atualizações quando o usuário tocar na notificação.

Atualizações de localização em segundo plano de longa duração vinculadas a áreas geográficas

Exemplo: o usuário quer ser notificado quando o dispositivo estiver próximo de um varejista.

Esse é um excelente caso de uso para a fronteira geográfica virtual. Como esse caso certamente envolve localização em segundo plano, use o método addGeofences(GeofencingRequest, PendingIntent).

Defina as opções de configuração a seguir:

  • Se estiver acompanhando transições de permanência, use o método setLoiteringDelay() transmitindo um valor de cinco minutos ou menos.

  • Use setNotificationResponsiveness(), transmitindo um valor de aproximadamente cinco minutos. No entanto, considere usar um valor de cerca de 10 minutos se o app puder gerenciar o atraso extra na responsividade.

Um app só pode registrar no máximo 100 fronteiras geográficas virtuais. Em um caso de uso em que um app quer acompanhar uma grande quantidade de opções de varejistas, pode ser necessário que ele registre uma ampla fronteira geográfica virtual (no nível da cidade). Além disso, ele precisará registrar dinamicamente fronteiras menores (para locais na cidade) para lojas dentro da fronteira maior. Quando o usuário insere uma fronteira geográfica virtual maior, fronteiras menores podem ser adicionadas. Uma vez que o usuário saia da fronteira maior, as menores podem ser removidas e as fronteiras, em geral, podem ser registradas novamente em uma nova área.

Atualizações de localização em segundo plano de longa duração sem um componente do app visível

Exemplo: um app que rastreia passivamente a localização

Se possível, use o método setPriority() com a opção PRIORITY_NO_POWER, porque isso não gera praticamente nenhum consumo de bateria. Caso o uso de PRIORITY_NO_POWER não seja possível, use PRIORITY_BALANCED_POWER_ACCURACY ou PRIORITY_LOW_POWER, mas evite usar PRIORITY_HIGH_ACCURACY para trabalho em segundo plano prolongado, uma vez que essa opção consome substancialmente a bateria.

Se precisar de mais dados de local, use a localização passiva, chamando o método setFastestInterval() transmitindo um valor menor do que o transmitido para setInterval(). A localização passiva, combinada com a opção PRIORITY_NO_POWER, pode entregar de maneira oportuna a localização calculada por outros apps sem custo extra.

Modere a frequência adicionando alguma latência. Para isso, use o método setMaxWaitTime(). Por exemplo, se você usar o método setinterval() com um valor em torno de 10 minutos, considere chamar setMaxWaitTime() com um valor entre 30 a 60 minutos. Ao usar essas opções, a localização é calculada para seu app a aproximadamente cada 10 minutos, mas o app só é ativado a cada 30 a 60 minutos com alguns dados de localização disponíveis como uma atualização em lote. Essa abordagem troca a latência por mais dados disponíveis e melhoria no desempenho da bateria.

Atualizações frequentes de alta precisão enquanto o usuário interage com outros apps

Exemplo: um app de navegação ou fitness que continua funcionando quando o usuário desliga a tela ou abre um app diferente.

Use um serviço em primeiro plano. Se houver a possibilidade de um trabalho caro ser feito pelo seu app em nome do usuário, é uma prática recomendada informá-lo sobre esse trabalho. Um serviço em primeiro plano requer uma notificação persistente. Para saber mais, consulte Visão geral das notificações.

Práticas recomendadas de localização

A implementação das práticas recomendadas nesta seção ajuda a reduzir o uso que seu app faz da bateria.

Remover atualizações de localização

Uma fonte comum de consumo desnecessário de bateria é a não remoção das atualizações de localização quando você não precisa mais delas. Isso pode acontecer, por exemplo, quando os métodos do ciclo de vida onStart() ou onResume() da atividade contêm uma chamada para requestlocationUpdates() sem uma chamada correspondente para removeLocationUpdates() nos métodos do ciclo de vida onPause() ou onStop().

Você pode usar componentes com reconhecimento de ciclo de vida para gerenciar melhor o ciclo de vida das atividades no seu app. Para mais informações, consulte Como gerenciar ciclos de vida com componentes que os reconhecem.

Definir limites de tempo

Para evitar consumo de bateria, defina um tempo limite razoável para a interrupção das atualizações de local. Esse tempo limite garante que as atualizações não continuem de forma indefinida, além de proteger o app em cenários onde as atualizações são solicitadas, mas não removidas (por exemplo, devido a um bug no código).

Para uma solicitação do provedor de localização combinada, adicione um tempo limite chamando setExpirationDuration(), que recebe um parâmetro responsável por apresentar o tempo em milissegundos desde a última vez em que o método foi chamado. Você também pode adicionar um tempo limite chamando setExpirationTime(), que recebe um parâmetro responsável por representar o tempo de expiração em milissegundos desde a última inicialização do sistema.

Para adicionar um tempo limite a uma solicitação de localização da fronteira geográfica virtual, chame o método setExpirationDuration().

Solicitações em lote

Para todos os casos de uso que não sejam em primeiro plano, agrupe várias solicitações em lotes. Você pode usar o método setInterval() para especificar o intervalo em que o cálculo da localização ocorrerá. Em seguida, use o método setMaxWaitTime() para definir o intervalo em que a localização será entregue ao app. O valor passado ao método setMaxWaitTime() deve ser um múltiplo do valor passado ao método setInterval(). Por exemplo, considere a solicitação de localização a seguir:

Kotlin

val request = LocationRequest()
request.setInterval(10 * 60 * 1000)
request.setMaxWaitTime(60 * 60 * 1000)

Java

LocationRequest request = new LocationRequest();
request.setInterval(10 * 60 * 1000);
request.setMaxWaitTime(60 * 60 * 1000);

Nesse caso, a localização é calculada, em média, a cada 10 minutos e cerca de seis pontos de dados de localização são entregues em um lote, aproximadamente a cada hora. Embora ainda receba atualizações de localização a cada 10 minutos, você economizará bateria porque seu dispositivo será ativado apenas a cada hora.

Usar atualizações de localização passivas

Nos casos de uso em segundo plano, é recomendável limitar as atualizações de localização. Os limites do Android 8.0 impõem essa prática, mas os apps que operam em dispositivos mais antigos precisam se esforçar para limitar ao máximo possível a localização em segundo plano.

É provável que, enquanto seu app esteja em segundo plano, outro app possa solicitar frequentemente atualizações de localização em primeiro plano. Os serviços de localização disponibilizam essas atualizações para seu app. Considere a seguinte solicitação de localização, que consome dados de localização de maneira oportuna:

Kotlin

val request = LocationRequest()
request.setInterval(15 * 60 * 1000)
request.setFastestInterval(2 * 60 * 1000)

Java

LocationRequest request = new LocationRequest();
request.setInterval(15 * 60 * 1000);
request.setFastestInterval(2 * 60 * 1000);

No exemplo anterior, a localização é calculada para o app aproximadamente a cada 15 minutos. Caso outros apps solicitem a localização, os dados serão disponibilizados para seu app em um intervalo máximo de dois minutos.

Embora o consumo de localização de forma passiva não incorra em gasto de bateria, tome muito cuidado nos casos em que o recebimento de dados de local acione operações pesadas de CPU ou E/S. Para minimizar os custos de bateria, o intervalo especificado em setFastestInterval() não pode ser pequeno demais.

Ao seguir as recomendações desta página, você pode melhorar de forma significativa o desempenho da bateria dos dispositivos dos seus usuários. É menos provável que os usuários excluam apps que não pesam na bateria.