Contenido del curso

Servicios de Localización

LocationTracker con StateFlow para rastreo en Android

Resumen

Cuando construyes una app que rastrea ubicación, no basta con observar coordenadas: necesitas controlar cuándo se activa, cuándo se pausa y cómo persisten los valores entre vistas. Aquí entra el patrón LocationTracker con StateFlow, una pieza clave para manejar el rastreo de localización en Android de forma eficiente y reactiva.

Qué es la clase LocationTracker y por qué la necesitas

Dentro de la carpeta domain/location creas una clase normal llamada LocationTracker. Su trabajo es orquestar el rastreo: decidir cuándo escuchar la ubicación del dispositivo y cuándo dejar de hacerlo. Para lograrlo, recibe por inyección dos dependencias.

La primera es el LocationObserver que viste en la clase anterior, encargado de emitir las coordenadas. La segunda es un ApplicationScope, que no es un scope cualquiera: se trata de un CoroutineScope declarado para persistir durante toda la vida de la aplicación, ideal para procesos en segundo plano que no deben morir con una pantalla [00:50].

¿Qué es un ApplicationScope en Android? Es un CoroutineScope que vive lo mismo que la app. Lo usas cuando una corrutina debe seguir activa aunque el usuario cambie de pantalla, como en el rastreo de ubicación.

Por qué usar MutableStateFlow en lugar de Flow normal

El corazón de LocationTracker son varios MutableStateFlow. La diferencia con un Flow normal es directa: los StateFlow retienen el último valor emitido, así que cuando una vista se conecta, recibe inmediatamente ese valor sin esperar a una nueva emisión [01:20].

A esto se le llama un hot flow. Puedes manipularlo en tiempo real asignándole el valor que necesites, mientras que los cold flows solo son secuencias que emiten cuando alguien los recolecta.

Qué propiedades expone el LocationTracker

Dentro de la clase declaras cuatro propiedades privadas como MutableStateFlow y las expones públicamente con .asStateFlow() para que nadie de afuera pueda modificarlas directamente.

  • locationData: arranca vacío y guarda la información de ubicación actual.
  • isTracking: un boolean que indica si la app está rastreando en este momento.
  • isObservingLocation: otro boolean, pero con un propósito distinto a isTracking.
  • elapsedTime: usa Duration.ZERO como valor inicial para llevar el tiempo transcurrido del recorrido.

Cada una se expone como un StateFlow inmutable hacia el exterior, manteniendo el control de las mutaciones dentro de la clase.

Cuál es la diferencia entre isTracking e isObservingLocation

Esta distinción es la que más confunde y la que más vale la pena entender. Aunque ambas son banderas booleanas, responden a preguntas diferentes sobre el estado de la app.

¿Cuándo uso isTracking y cuándo isObservingLocation? Usa isTracking para saber si el usuario le dio play o pausa al recorrido. Usa isObservingLocation para saber si la app cumple las condiciones técnicas (permisos, servicios) para acceder a la ubicación.

En otras palabras, isTracking responde a la intención del usuario, mientras que isObservingLocation responde a si los permisos están habilitados y los componentes del sistema están listos para entregar coordenadas [02:45].

Cómo modificar los valores de los StateFlow con funciones

Para cambiar los valores de estos flows no los expones mutables hacia afuera. En su lugar, defines funciones públicas que acceden a la propiedad .value del MutableStateFlow y la actualizan de forma controlada.

  • setIsTracking(isTracking: Boolean): recibe un boolean y actualiza el valor de la propiedad interna.
  • startObservingLocation(): pone isObservingLocation.value = true para activar el rastreo.
  • stopObservingLocation(): pone isObservingLocation.value = false para detenerlo.
  • updateLocationData(): reservada para actualizar la ubicación cuando haya fotos disponibles, algo que se conectará con la sección de cámara más adelante.

Este patrón mantiene el principio de encapsulamiento: el estado solo cambia a través de funciones explícitas, lo que hace el código predecible y fácil de testear.

Cómo finalizar un recorrido con finishTracking

La función finishTracking() cierra el ciclo completo del rastreo. Hace cuatro cosas en orden para dejar la app en un estado limpio.

  1. Llama a stopObservingLocation() para cortar la escucha de coordenadas.
  2. Ejecuta setIsTracking(false) para marcar que el recorrido terminó.
  3. Resetea elapsedTime.value a Duration.ZERO para reiniciar el cronómetro.
  4. Vuelve a inicializar locationData.value con un LocationData vacío.

Un detalle de refactor que aparece en el camino: la propiedad inicialmente llamada location se renombra a locationData con clic secundario y Refactor → Rename, para que el nombre comunique con precisión que se trata del paquete completo de información de ubicación, no solo de coordenadas sueltas.

Por qué este patrón es la base del rastreo en tiempo real

Lo que acabas de construir es el estado central que cualquier ViewModel o pantalla podrá observar. La clase no rastrea por sí misma todavía: define las palancas que se accionarán en la próxima clase, donde estos StateFlow entran en acción combinados con corrutinas y el LocationObserver.

La lógica queda separada en capas claras: LocationObserver produce datos, LocationTracker decide cuándo escuchar y guarda el estado, y la UI solo consume StateFlow expuestos. ¿Has trabajado antes con StateFlow en proyectos propios? Cuéntame en los comentarios cómo lo usaste.

      LocationTracker con StateFlow para rastreo en Android