Cómo optimizar la ubicación para ahorrar batería

Los límites de ubicación en segundo plano, que se introdujeron en Android 8.0 (nivel de API 26), renovaron el enfoque en relación con el modo en que el uso de los servicios de ubicación afecta el agotamiento de la batería. En esta página, se abordan algunas de las prácticas recomendadas para los servicios de ubicación y lo que puedes hacer ahora para que tus apps usen la batería de manera más eficiente. Seguir estas prácticas recomendadas beneficiará a tu app, sin importar la versión de la plataforma en la que se está ejecutando.

En Android 8.0, los límites de ubicación en segundo plano introdujeron los siguientes cambios:

  • Se limita la recopilación de la ubicación en segundo plano, se calcula la ubicación en primer plano y se envían los datos correspondientes solo unas pocas veces por hora.
  • Las búsquedas Wi-Fi son más eficientes y no se calculan las actualizaciones de ubicación cuando el dispositivo permanece conectado al mismo punto de acceso estático.
  • La capacidad de respuesta del geovallado cambia de decenas de segundos a aproximadamente dos minutos. Este cambio mejora notablemente el rendimiento de la batería (hasta 10 veces) en algunos dispositivos.

En esta página, se supone que ya utilizas las API de Servicios de ubicación de Google, que ofrecen mayor precisión e imponen una carga de batería más ligera que la de las API de ubicación del framework. En particular, en esta página se asume que conoces la API del proveedor de ubicación combinada, que combina señales de GPS, Wi-Fi y redes móviles, así como datos de acelerómetros, giroscopios, magnetómetros y otros sensores. También deberías conocer la API de geovallado, que está diseñada en función de la API del proveedor de ubicación combinada y optimizada para el rendimiento de la batería.

Cómo interpretar el agotamiento de la batería

La recopilación de la ubicación y el agotamiento de la batería se relacionan de forma directa en los siguientes aspectos:

  • Precisión: Es la precisión de los datos de ubicación. En general, cuanto mayor sea la precisión, mayor será el agotamiento de la batería.
  • Frecuencia: Indica con qué frecuencia se calcula la ubicación. Cuanto más frecuente sea el cálculo de la ubicación, mayor será el uso de la batería.
  • Latencia: Es la rapidez con la que se entregan los datos de ubicación. Por lo general, una menor latencia requiere más batería.

Precisión

Puedes especificar la precisión de la ubicación utilizando el método setPriority(), pasando uno de los siguientes valores como argumento:

  • PRIORITY_HIGH_ACCURACY proporciona la ubicación más precisa posible, que se calcula usando tantas entradas como sea necesario (GPS, Wi-Fi y red móvil, y utiliza una variedad de sensores), y puede causar un agotamiento significativo de la batería.
  • PRIORITY_BALANCED_POWER_ACCURACY proporciona una ubicación precisa al mismo tiempo que optimiza el consumo. Rara vez utiliza el GPS. Por lo general, usa una combinación de información de Wi-Fi y redes móviles para calcular la ubicación del dispositivo.
  • PRIORITY_LOW_POWER depende, en gran medida, de las torres de telefonía celular y evita las entradas de GPS y Wi-Fi, lo que proporciona una gran precisión (al nivel de una ciudad) con un consumo mínimo de batería.
  • PRIORITY_NO_POWER recibe ubicaciones de forma pasiva de otras apps para las que esta ya se haya calculado.

Es posible satisfacer las necesidades de ubicación de la mayoría de las apps utilizando opciones de consumo equilibrado o de bajo consumo. La precisión alta debería reservarse para las apps que se están ejecutando en primer plano y que requieren actualizaciones de ubicación en tiempo real (por ejemplo, una app de mapas).

Frecuencia

Puedes especificar la frecuencia de ubicación utilizando dos métodos:

  • Utiliza el método setinterval() para especificar el intervalo en el que se calcula la ubicación para tu app.
  • Utiliza el método setFastestInterval() para especificar el intervalo en el que se entrega a tu app la ubicación que se calcula para otras.

Debes pasar el valor más alto posible cuando uses setInterval(). Ten en cuenta eso especialmente cuando se recopilen datos de la ubicación en segundo plano, lo que suele agotar la batería de una manera no deseada. Se debe reservar el uso de intervalos de unos pocos segundos para los casos prácticos en primer plano. Los límites de ubicación en segundo plano introducidos en Android 8.0 refuerzan estas estrategias, pero tu app debería esforzarse por aplicarlas en dispositivos que ejecuten Android 7.0 o versiones anteriores.

Latencia

Puedes especificar la latencia utilizando el método setMaxWaitTime(), pasando un valor que es varias veces mayor que el intervalo especificado en el método setInterval(). Esta configuración retrasa la entrega de la ubicación, y las actualizaciones de varias ubicaciones pueden entregarse en lotes. Estos dos cambios ayudan a minimizar el consumo de batería.

Si tu app no necesita una actualización de ubicación al instante, deberías pasar el mayor valor posible al método setMaxWaitTime(), cambiando de manera efectiva la latencia por más datos y eficiencia de la batería.

Si se usa el geovallado, las apps deberían pasarle un valor alto al método setNotificationResponsiveness() para preservar batería. Se recomienda un valor de cinco minutos o más.

Casos prácticos de la ubicación

En esta sección, se describen algunos casos típicos de recopilación de ubicación, junto con recomendaciones para el uso óptimo del geovallado y las API de proveedores de ubicación combinada.

Actualizaciones visibles para el usuario o en primer plano

Ejemplo: una app de mapas que necesita actualizaciones frecuentes y precisas con una latencia muy baja. Todas las actualizaciones se realizan en primer plano: el usuario inicia una actividad, consume datos de ubicación y, luego, detiene la actividad al poco tiempo.

Usa el método setPriority() con un valor de PRIORITY_HIGH_ACCURACY o PRIORITY_BALANCED_POWER_ACCURACY.

El intervalo especificado en el método setInterval() depende del caso práctico: para situaciones en tiempo real, establece el valor en unos pocos segundos; en caso contrario, limítalo a unos pocos minutos (se recomiendan aproximadamente dos minutos o más para minimizar el uso de la batería).

Cómo detectar la ubicación del dispositivo

Ejemplo: una app meteorológica quiere conocer la ubicación del dispositivo.

Utiliza el método getLastLocation(), que muestra la ubicación más reciente disponible (que en raras ocasiones puede ser nula). Este método proporciona una forma sencilla de obtener la ubicación y no implica costos asociados con la solicitud activa de actualizaciones de ubicación. Utilízalo junto con el método isLocationAvailable(), que mostrará true cuando la ubicación que muestre getLastLocation() esté actualizada razonablemente.

Cómo iniciar actualizaciones cuando un usuario se encuentra en una ubicación específica

Ejemplo: se solicitan actualizaciones cuando un usuario se encuentra a cierta distancia del trabajo, de su casa o de otra ubicación.

Utiliza el geovallado junto con las actualizaciones de proveedores de ubicación combinada. Se solicitan actualizaciones cuando la app recibe un activador de entrada de geovallado y se quitan cuando recibe uno de salida. Esto garantiza que la app reciba actualizaciones de ubicación más detalladas solo cuando el usuario entre en un área definida.

El flujo de trabajo típico para esta situación podría implicar la aparición de una notificación en el momento en que el geovallado entra en transición y el lanzamiento de una actividad que contiene código para solicitar actualizaciones cuando el usuario presiona la notificación.

Cómo iniciar actualizaciones basadas en el estado de actividad del usuario

Ejemplo: se solicitan actualizaciones solo cuando el usuario está conduciendo o andando en bicicleta.

Utiliza la API de reconocimiento de actividad junto con las actualizaciones de proveedores de ubicación combinada. Solicita actualizaciones cuando se detecte la actividad de destino y quítalas cuando el usuario deje de realizar esa actividad.

El flujo de trabajo típico para este caso práctico podría implicar la aparición de una notificación de la actividad detectada y el lanzamiento de una actividad que contenga código para solicitar actualizaciones cuando el usuario presione la notificación.

Actualizaciones de ubicación a largo plazo en segundo plano vinculadas a áreas geográficas

Ejemplo: el usuario desea que se lo notifique cuando el dispositivo se encuentre cerca de una tienda específica.

Este es un excelente caso de uso para el geovallado. Debido a que, casi con toda seguridad, el caso práctico implica una ubicación en segundo plano, utiliza el método addGeofences(GeofencingRequest, PendingIntent).

Debes establecer las siguientes opciones de configuración:

  • Si estás realizando seguimientos de transiciones de permanencia, utiliza el método setLoiteringDelay() pasando un valor de aproximadamente cinco minutos o menos.

  • Utiliza setNotificationResponsiveness() y pasa un valor de aproximadamente cinco minutos. Sin embargo, considera usar un valor de aproximadamente diez minutos si tu app puede administrar el retraso adicional en la respuesta.

Una app solo puede registrar un máximo de 100 geovallados a la vez. En un caso práctico en el que una app desea rastrear un gran número de opciones de tiendas, la app puede registrar un geovallado grande (al nivel de una ciudad) y registrar dinámicamente geovallados más pequeños (en ubicaciones dentro de la ciudad) para tiendas que estén dentro del más grande. Cuando el usuario entra en un geovallado grande, se pueden agregar otros más pequeños; cuando sale del geovallado grande, se pueden quitar los pequeños y se pueden volver a registrar para su uso en una nueva área.

Actualizaciones de ubicación a largo plazo en segundo plano sin un componente de app visible

Ejemplo: una app que rastrea la ubicación de forma pasiva

Si es posible, usa el método setPriority() con la opción PRIORITY_NO_POWER, ya que casi no consume batería. Si no es posible utilizar PRIORITY_NO_POWER, usa PRIORITY_BALANCED_POWER_ACCURACY o PRIORITY_LOW_POWER, pero evita usar PRIORITY_HIGH_ACCURACY para un trabajo sostenido en segundo plano, ya que esta opción agota considerablemente la batería.

Si necesitas más datos de ubicación, utiliza la ubicación pasiva llamando al método setFastestInterval() y pasando un valor menor que el que le pasas a setInterval(). Cuando se combina con la opción PRIORITY_NO_POWER, la ubicación pasiva puede entregar de manera oportuna la ubicación que calculen otras apps sin costo adicional.

Modera la frecuencia agregando latencia con el método setMaxWaitTime(). Por ejemplo, si utilizas el método setinterval() con un valor aproximado de 10 minutos, deberías considerar llamar a setMaxWaitTime() con un valor entre 30 y 60 minutos. Usando estas opciones, se calcula la ubicación para tu app aproximadamente cada 10 minutos, pero solo se activa cada 30 a 60 minutos con algunos datos de ubicación disponibles como una actualización por lotes. Este enfoque cambia la latencia para obtener más datos disponibles y un mejor rendimiento de la batería.

Actualizaciones frecuentes de precisión alta mientras el usuario interactúa con otras apps

Ejemplo: una app de navegación o entrenamiento que continúa funcionando cuando el usuario apaga la pantalla o abre otra app.

Utiliza un servicio en primer plano. Si tu app va a realizar un trabajo demandante en nombre del usuario, se recomienda que el usuario sea consciente de ello. Un servicio en primer plano requiere una notificación persistente. Para obtener más información, consulta la sección Descripción general de las notificaciones.

Prácticas recomendadas para la ubicación

Implementar las prácticas recomendadas que se describen en esta sección ayuda a reducir el uso de la batería de tu app.

Quita actualizaciones de ubicación

Una fuente común de agotamiento innecesario de la batería es la falta de eliminación de las actualizaciones de ubicación cuando ya no son necesarias. Esto puede ocurrir, por ejemplo, cuando el objeto onStart() de una actividad o métodos de ciclo de vida onResume() contienen una llamada a requestlocationUpdates() sin una llamada correspondiente a removeLocationUpdates() en los métodos de ciclo de vida onPause() o onStop().

Puedes utilizar componentes que prioricen los ciclos de vida para administrar mejor el ciclo de vida de las actividades de tu app. Para obtener más información, consulta Cómo controlar ciclos de vida con componentes que los priorizan.

Establece tiempos de espera

Para evitar el agotamiento de la batería, establece un tiempo de espera razonable cuando deban detenerse las actualizaciones de ubicación. El tiempo de espera garantiza que las actualizaciones no continúen indefinidamente y protege a la app en situaciones donde se solicitan actualizaciones, pero no se las quita (por ejemplo, debido a un error en el código).

Para una solicitud de proveedor de ubicación combinada, agrega un tiempo de espera llamando a setExpirationDuration(), que recibe un parámetro que representa el tiempo en milisegundos desde la última vez que se llamó al método. También puedes agregar un tiempo de espera llamando a setExpirationTime(), que recibe un parámetro que representa el tiempo de vencimiento en milisegundos desde el último inicio del sistema.

Para agregar un tiempo de espera a una solicitud de ubicación de geovallado, llama al método setExpirationDuration().

Utiliza solicitudes por lotes

Para todos los casos prácticos que no sean en primer plano, agrupa varias solicitudes al mismo tiempo. Puedes usar el método setInterval() para especificar el intervalo en el que quieres que se calcule la ubicación. Luego, utiliza el método setMaxWaitTime() para establecer el intervalo en el que se entrega la ubicación a tu app. El valor que se pase al método setMaxWaitTime() debería ser múltiplo del que se pasa a setInterval(). Por ejemplo, considera la siguiente solicitud de ubicación:

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);

En este caso, la ubicación se calcula aproximadamente cada diez minutos y se entregan alrededor de seis puntos de datos de ubicación en un lote por hora. Si bien todavía recibes actualizaciones de ubicación cada diez minutos aproximadamente, consumes menos batería porque el dispositivo se activa solamente una vez por hora.

Usa actualizaciones de ubicación pasiva

En casos prácticos en segundo plano, es una buena idea limitar las actualizaciones de ubicación. Los límites de Android 8.0 imponen esta práctica, pero las apps que se ejecutan en dispositivos más antiguos deben esforzarse por limitar la ubicación en segundo plano tanto como sea posible.

Es probable que mientras tu app esté en segundo plano, otra app solicite con frecuencia actualizaciones de ubicación en primer plano. Los servicios de ubicación ponen estas actualizaciones a disposición de tu app. Considera usar la siguiente solicitud, que consume datos de ubicación de forma 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);

En el ejemplo anterior, se calcula la ubicación para tu app aproximadamente cada 15 minutos. Si otras apps solicitan la ubicación, los datos se ponen a disposición de tu app en un intervalo máximo de dos minutos.

Si bien consumir datos de la ubicación de forma pasiva no genera un agotamiento de la batería, ten especial cuidado en los casos en que recibir datos de ubicación desencadene operaciones demandantes de CPU o de E/S. Para minimizar los costos de la batería, el intervalo especificado en setFastestInterval() no debería ser demasiado pequeño.

Puedes mejorar significativamente el consumo de la batería de los dispositivos de los usuarios siguiendo las recomendaciones de esta página. Recuerda que es menos probable que los usuarios borren las apps que no agotan la batería.