Preguntas frecuentes
Permisos generales y bloqueos
¿Qué pasa si no agregamos ningún permiso? ¿Se estrellará?
No, Tracelet no fallará. Tracelet está diseñado para ser altamente resistente. Si intenta llamar a Tracelet.start() sin declarar o solicitar los permisos de ubicación obligatorios, Tracelet detectará correctamente la SecurityException, registrará un error detallado en la consola y emitirá un evento de error a sus oyentes. La aplicación seguirá ejecutándose normalmente. Sin embargo, no se registrarán ubicaciones hasta que se otorguen los permisos.
¿Qué sucede si no habilitamos el permiso de movimiento (actividad física)?
No, no fallará. Si no se concede el permiso ACTIVITY_RECOGNITION (Android) o Motion & Fitness (iOS), Tracelet volverá automáticamente al seguimiento estándar basado en la distancia.
Sin embargo, el consumo de batería aumentará significativamente. Sin detección de movimiento, Tracelet no puede poner el hardware del GPS en modo de suspensión cuando el dispositivo está parado. Se recomienda encarecidamente solicitar este permiso para aplicaciones de producción para garantizar una duración óptima de la batería.
Compilación y configuración de iOS
La compilación de iOS falla con errores de Rust/UniFFI de “símbolo no definido” (_ffi_tracelet_core_rustbuffer_free, _uniffi_tracelet_core_checksum_method_*)
Habilite la integración del Administrador de paquetes Swift de Flutter. El núcleo Rust de Tracelet (TraceletCore.xcframework, que expone los símbolos UniFFI utilizados por tracelet_ios) está vinculado a través del Administrador de paquetes Swift. En la ruta heredada exclusiva de CocoaPods, el marco no está vinculado al objetivo tracelet_ios, por lo que el vinculador de Xcode informa símbolos indefinidos como _ffi_tracelet_core_rustbuffer_free y _uniffi_tracelet_core_checksum_method_*.
Arreglarlo con:
flutter config --enable-swift-package-manager
flutter clean
flutter pub get
flutter run # or: flutter build iosNotas:
- Compile/ejecute desde Flutter CLI o su IDE (Código VS), que impulsa la compilación compatible con SPM, en lugar de abrir
.xcworkspacey compilar directamente desde Xcode. - Esta es una configuración global de Flutter única; no es necesario cambiarlo por proyecto.
flutter clean+ unpod installnuevo por sí solo no lo resolverá: la pieza que falta es SPM que vincula el marco de Rust, no Pods obsoletos.
Batería y sensores de movimiento
¿El sensor de movimiento consume más batería al caminar?
No, en realidad ahorra batería. Los sensores de movimiento de hardware (acelerómetro/detector de pasos) consumen menos de la batería de 0.1% por hora. Tracelet utiliza este sensor de energía ultrabaja para apagar completamente el chip GPS que consume mucha energía (que consume 4-8% por hora) cada vez que el teléfono está apoyado sobre un escritorio.
Cuando empiezas a caminar, el sensor de movimiento activa inmediatamente el GPS para registrar el viaje. El resultado general es un enorme beneficio neto para la duración de la batería en comparación con el seguimiento tradicional.
¿Por qué no recibo actualizaciones continuas cuando el teléfono no se mueve?
Esto es intencional: es el mayor ahorro de batería que ofrece Tracelet. Cuando la detección de movimiento determina que el dispositivo ha estado quieto, Tracelet apaga el GPS continuo y cambia a un modo de bajo consumo (correcciones periódicas de un solo disparo o monitoreo de geovallas). En el momento en que se reanuda el movimiento real, el seguimiento continuo se activa automáticamente.
Si aún desea una ubicación periódica de “Estoy aquí” mientras está parado, configure heartbeatInterval (segundos). Una pequeña desviación del GPS mientras está estacionado no infla el odómetro; las correcciones peores que odometerAccuracyThreshold (50 m predeterminado) están excluidas de la distancia.
¿Cómo puedo reducir aún más el uso de la batería?
Tracelet ya duerme el GPS cuando está parado, pero tienes varias palancas:
- Presupuesto de batería: configure
batteryBudgetPerHourenGeoConfig(por ejemplo,3.0para 3 %/hora). Luego, Tracelet ajusta automáticamentedistanceFilterydesiredAccuracyen tiempo de ejecución para permanecer por debajo de ese objetivo. - Lanzamiento de Wakelock: configura
releaseWakelockWhenStationary: trueenAndroidConfigpara permitir que la CPU entre en modo de suspensión total cuando el usuario está estacionario (requiereMotionDetectionMode.smart). - Filtro de distancia: un
distanceFilter(metros) más grande registra menos correcciones mientras se mueve. - Menor precisión:
desiredAccuracydemedium/lowconsume menos energía quehigh/best. - Modo periódico: para casos de uso de “dónde están aproximadamente”, las correcciones periódicas únicas (
startPeriodic) son considerablemente más económicas que el seguimiento continuo. - Mantener el permiso de movimiento concedido: sin
ACTIVITY_RECOGNITIONTracelet no puede desactivar el GPS de forma tan agresiva.
¿Cuál es la diferencia entre las opciones motionDetectionMode?
motionDetectionMode decide cómo Tracelet detecta el movimiento para iniciar/detener el GPS:
accelerometer: utiliza los sensores de movimiento del hardware (y el reconocimiento de actividad cuando esté permitido). Mínima potencia, funciona en interiores y sin conexión GPS.speed: utiliza únicamente la velocidad del GPS. Simple y predecible, pero necesita una posición de GPS para notar que te has detenido, por lo que reacciona más lento y usa más energía.smart— combina ambos: permanece en seguimiento continuo si ya sea el acelerómetro o la velocidad del GPS indican que te estás moviendo, y solo se detiene cuando ambos coinciden en que te has detenido. Más robusto contra transiciones falsas; Recomendado para la mayoría de las aplicaciones.
Servicios de ubicación y estados de seguimiento
¿Qué sucede si el usuario desactiva los servicios de ubicación mientras el seguimiento está activo?
Respuesta corta: Tracelet no detiene, bloquea ni derriba el seguimiento. Mantiene activada la sesión de seguimiento, emite un evento providerchange para que su aplicación pueda reaccionar, no registra ubicaciones nuevas mientras la ubicación está desactivada (con dos excepciones específicas de la plataforma a continuación) y reanuda automáticamente la entrega de ubicaciones en el momento en que el usuario vuelve a habilitar la ubicación, en todos los estados (primer plano, fondo, finalizado). No es necesario que vuelvas a llamar a start().
“Servicios de ubicación desactivados” aquí significa el cambio de ubicación a nivel del sistema operativo (Android: Configuración → Ubicación; iOS: Configuración → Privacidad → Servicios de ubicación). Revocar el permiso de la aplicación es un caso relacionado pero separado; consulte la nota al final de esta respuesta.
como detectarlo
final sub = tl.Tracelet.onProviderChange((tl.ProviderChangeEvent e) {
if (!e.enabled) {
// Location services were turned OFF — prompt the user / show a banner.
} else {
// Back ON — Tracelet has already resumed; no action required.
}
});ProviderChangeEvent transporta enabled, status (autorización), gps, network, accuracyAuthorization, gpsFallback y mockLocationsDetected. El mismo evento se entrega a la devolución de llamada sin cabeza en el estado de fondo/inactivo (si está registrado) y se conserva como un registro providerchange a menos que configure disableProviderChangeRecord: true.
Comportamiento por estado y plataforma
| Estado | Androide | iOS |
|---|---|---|
| Primer plano | incendios providerchange (enabled: false); el servicio de primer plano permanece vivo; no hay nuevas correcciones hasta que se vuelva a habilitar. | providerchange incendios; didFailWithError se maneja con elegancia (los one-shots retroceden a la última ubicación conocida); no hay nuevas correcciones. |
| Fondo | El servicio en primer plano (y su notificación) sigue ejecutándose; las actualizaciones fusionadas simplemente se detienen; providerchange aún enviado. Se reanuda al volver a habilitar. | La suscripción CLLocationManager permanece registrada; iOS no entrega nada hasta que la ubicación regresa, luego se reanuda (y puede reiniciar la aplicación a través de la región/SLC). |
| Terminado (muerto) | Si una sesión en segundo plano/arranque está activa (stopOnTerminate: false / startOnBoot), el servicio se comporta como En segundo plano arriba. Si el proceso no está activo, no se ejecuta nada hasta que el sistema operativo lo inicie nuevamente. | iOS reinicia la aplicación a través del monitoreo de ubicación/región importante solo cuando ocurre un evento de ubicación/región, lo cual no puede hacer mientras la ubicación está desactivada. Una vez que se vuelva a habilitar, el siguiente evento de clasificación se reinicia y se reanuda. |
En todos los casos, el estado de la sesión se conserva, por lo que al volver a habilitar la ubicación se reanuda el seguimiento automáticamente sin reinicialización.
Lo que Tracelet no hace
- No detiene automáticamente la sesión ni borra su configuración/estado.
- No se lanza ni se bloquea: se detecta el error de “ubicación desactivada/denegada” de la plataforma.
- No fabrica ubicaciones (excepto la navegación a estima de Android, a continuación): su base de datos simplemente tiene un espacio durante el período en que la ubicación estuvo desactivada.
Detalles específicos de la plataforma que vale la pena conocer
Android: si el usuario desactiva GPS pero el posicionamiento Wi-Fi/celular aún está activado, Tracelet automáticamente vuelve al posicionamiento de potencia equilibrada y emite providerchange con gpsFallback: true, restaurando la precisión total cuando regresa el GPS. Esas correcciones aproximadas son grabadas y sincronizadas (etiquetadas con locationSource y su accuracy real). Si enableDeadReckoning: true, después de perder el GPS durante el retraso configurado, Tracelet estima las posiciones de los sensores de movimiento hasta que regresa una solución real. La notificación persistente permanece visible en todo momento.
iOS: un requestLocation() fallido resuelve solicitudes únicas con la última ubicación conocida en lugar de colgarse. iOS no ofrece eventos en segundo plano ni de reinicio mientras la ubicación está desactivada; la entrega y el relanzamiento del estado eliminado se reanudan una vez que se vuelve a encender.
Qué debes hacer en tu aplicación
- Suscríbase a
onProviderChangey muestre un banner/diálogo cuandoenabled == false. - Opcionalmente, guíe al usuario a la configuración a través de
Tracelet.openLocationSettings(). - No llame nuevamente a
start()al volver a habilitarlo: Tracelet se reanuda por sí solo; llamar astart()es inofensivo pero innecesario.
Revocar el permiso de la aplicación versus desactivar la opción
Desactivar alternar afecta a todas las aplicaciones y es completamente recuperable como se indicó anteriormente. La revocación del permiso de ubicación de la aplicación (o la degradación de “Siempre” → “Mientras está en uso”) se informa a través del mismo evento providerchange a través de los campos status / accuracyAuthorization. En Android 12+, si se pierde el permiso de ubicación en segundo plano, se omite intencionalmente un inicio/reinicio en segundo plano (de lo contrario, fallaría silenciosamente): vuelva a otorgar el permiso y reinicie para reanudar.
¿Qué tan precisas son las ubicaciones y cómo distingo el GPS de las conexiones Wi-Fi/celular?
Cada Location lleva un coords.accuracy real en metros y una etiqueta locationSource: "gps" (≤50 m), "wifi" (≤200 m), "cell" (peor) o "network" (durante el respaldo del GPS). Tracelet no deja caer silenciosamente correcciones de baja precisión (las registra para que su recorrido se mantenga continuo), pero mantiene las correcciones deficientes fuera del odómetro (odometerAccuracyThreshold, 50 m predeterminado) y rechaza saltos de velocidad imposible (maxImpliedSpeed).
Si solo desea datos con calidad GPS, filtre por locationSource == "gps" o accuracy <= 50. Si el usuario solo otorgó una ubicación aproximada/aproximada (o iOS “Preciso: desactivado”), cada corrección es aproximada según la política del sistema operativo; verifique accuracyAuthorization / reducedAccuracy.
Mi track salta de izquierda a derecha mientras camino y la distancia es entre 2 y 3 veces mayor que en Strava. ¿Cómo lo soluciono?
El ruido del GPS se registra como movimiento real. A un ritmo de caminata (~1,4 m/s), el error horizontal de un teléfono es de 5 a 8 m bajo el cielo abierto y considerablemente peor bajo la cobertura de árboles o entre edificios: una gran fracción de la distancia que realmente se movió el usuario entre dos posiciones. Cada “pico” de inquietud en el costado es un cambio de coordenadas genuino, y un odómetro que confía en cada corrección lo suma todo. Las aplicaciones de fitness se ven más fluidas porque se ajustan específicamente al caso de estar a pie; Los valores predeterminados del SDK son deliberadamente neutrales entre vehículos, ciclistas y peatones.
Primero, actualice a 3.8.0-beta o posterior. Dos defectos en 3.7.6 o menos produjeron exactamente este síntoma y no se pudieron corregir:
useKalmanFilter: truesuavizó sólo la pista renderizada. El suavizado se ejecutó después del procesador de ubicación y solo alimentócoords, por lo que el odómetro siguió integrando fluctuaciones brutas: el mapa parecía limpio mientras que la distancia seguía siendo incorrecta. Ahora funciona antes de los filtros, por lo que la distancia, las puertas de precisión/velocidad implícita y el odómetro ven la pista sin ruido. Espere que las distancias registradas disminuyan después de la actualización; esa gota es el ruido que estabas contando anteriormente.- Una solución demasiado aproximada para
odometerAccuracyThresholdse mantuvo correctamente fuera del odómetro, pero el punto de referencia del odómetro avanzó hacia él de todos modos, por lo que el terreno cubierto durante ese segmento se perdió permanentemente. Apretar la puerta hizo que la distancia faltara en lugar de arreglarla. Una solución aproximada ahora difiere su distancia, y la siguiente solución confiable reserva todo el tramo.
Entonces deja de adivinar umbrales; deja que el clasificador del dispositivo los elija:
await tl.Tracelet.ready(tl.Config(
geo: tl.GeoConfig(
filter: tl.LocationFilter(useKalmanFilter: true),
),
classifier: tl.ClassifierConfig(
enableFusedClassifier: true, // required — the classifier must be running
autoTuneFromTransportMode: true,
),
));El clasificador fusiona la cadencia del acelerómetro con la velocidad del GPS para separar still / walking / running / cycling / vehicle, y cuando un modo se confirma, intercambia distanceFilter, trackingAccuracyThreshold, odometerAccuracyThreshold y maxImpliedSpeed por valores adecuados para él. Consulte ajuste automático por modo de transporte para conocer las garantías (solo cambios confirmados, umbrales intercambiados para que el odómetro permanezca continuo, unknown restaura sus propios valores).
Con debug: true y logLevel: verbose verás cada resintonización en el registro:
auto-tune: 'walking' → distanceFilter=8.0m trackingAccuracy=15m odometerAccuracy=10m maxImpliedSpeed=4m/s¿Existen ajustes preestablecidos recomendados para caminar, trotar y correr?
Estos son los valores exactos a los que se aplica el ajuste automático, por lo que puedes configurarlos manualmente si tu aplicación solo rastrea una actividad (un rastreador de carreras, una aplicación de entrega a pie) y prefieres no ejecutar el clasificador:
| Actividades | distanceFilter | trackingAccuracyThreshold | odometerAccuracyThreshold | maxImpliedSpeed |
|---|---|---|---|---|
| Estacionario | 25 metros | 15 metros | 10 metros | 3 m/s |
| Caminando | 8 metros | 15 metros | 10 metros | 4 m/s |
| Trotar / correr | 12 metros | 25 metros | 15 metros | 9 m/s |
| Ciclismo | 20 metros | 30 metros | 20 metros | 20 m/s |
| Conducir | 30 metros | 50 metros | 30 metros | 60 m/s |
Jogging no es un ajuste preestablecido separado. Se encuentra dentro de la banda de carrera (6-20 km/h) y comparte sus umbrales; un “preajuste para trotar” distinto de correr sería una precisión inventada, no una medición.
Una aplicación solo para caminar, con suavizado activado:
geo: tl.GeoConfig(
distanceFilter: 8,
filter: tl.LocationFilter(
useKalmanFilter: true,
trackingAccuracyThreshold: 15,
odometerAccuracyThreshold: 10,
maxImpliedSpeed: 4,
),
),Si ya lo afinaste a mano y todavía tienes aproximadamente un 50 % de duración, verifica estas tres cosas:
- UN
distanceFilterpor debajo del nivel de ruido del GPS admite ruido como distancia. EndistanceFilter: 5, un error de 6 m estacionario se interpreta como 6 m de caminata. Manténgalo a ~8 m o más a pie; contrariamente a la intuición, registrar menos puntos da un total más preciso. - Un
trackingAccuracyThresholdajustado hace que la puerta del odómetro sea irrelevante. ContrackingAccuracyThreshold: 10, cada punto que sobrevive ya es ≤ 10 m, por lo queodometerAccuracyThreshold(50m predeterminado) nunca rechaza nada. La puerta con la que contabas no hace nada; el error proviene de las correcciones que mantuvo, no de las que eliminó. useKalmanFilterestá desactivado de forma predeterminada, y antes de3.8.0no afectaba la distancia en absoluto, incluso cuando estaba activado.
No espere una coincidencia exacta con otra aplicación. Strava, Google Fit y Tracelet consumen diferentes flujos de corrección (cadencia de muestreo, configuración del proveedor, si la aplicación estaba en primer plano) y aplican un suavizado diferente. El objetivo realista es llegar a un acuerdo dentro de un pequeño porcentaje en una ruta de longitud conocida: medir con una ruta que pueda verificar, no con el número de otra aplicación.
¿Por qué getCurrentPosition() falla con LOCATION_FAILURE en algunos teléfonos pero funciona en otros?
PlatformException(LOCATION_FAILURE, "Failed to obtain location") significa que la solicitud única no pudo obtener una nueva solución dentro de timeout y no tenía una ubicación en caché a la cual recurrir. No es un error en su código: es que el GPS/pila fusionada del dispositivo no logra entregar una solución a tiempo. Un one-shot de alta precisión solicita una solución nueva, y si eso se logra dentro de (por ejemplo) 30 s depende en gran medida del dispositivo y el entorno:
- “Precisión de ubicación de Google” está desactivada — Configuración → Ubicación → Servicios de ubicación → Precisión de ubicación de Google (escaneo de Wi-Fi/Bluetooth). Cuando está activado, el proveedor fusionado devuelve una conexión de Wi-Fi/celular en el interior casi al instante; cuando está apagado, el teléfono debe esperar una señal de GPS sin procesar que nunca llega al interior. Esta es la causa número uno de “funciona en mi teléfono, no en el de ellos”.
- Interiores / subterráneos / sin vista del cielo: una localización GPS en frío necesita visibilidad del cielo, y los conjuntos de chips económicos pueden exceder los 30 s para una primera localización en frío (TTFF), mientras que los buques insignia obtienen localizaciones de GPS asistido en segundos.
- Proveedor de GPS deshabilitado a nivel del sistema operativo (ubicación solo de red): una solicitud pura de alta precisión no tiene nada que bloquear.
- Servicios de Google Play faltantes/obsoletos (algunas compilaciones de Huawei/AOSP): el cliente fusionado no se puede ejecutar.
- Recuento de muestras: con
samples: 3Tracelet debe recopilar tres correcciones; en señal marginal puede obtener uno y expirar antes que el resto.samples: 1es más tolerante en interiores.
Cómo hacerlo confiable: recurra a la última ubicación conocida:
Future<tl.Location?> bestPosition() async {
try {
return await tl.Tracelet.getCurrentPosition(
desiredAccuracy: tl.DesiredAccuracy.high,
timeout: 60, // cold GPS fixes on budget phones can exceed 30 s
samples: 1, // more forgiving indoors than 3
maximumAge: 30000, // accept a <30 s-old cached fix instantly
);
} on PlatformException catch (e) {
if (e.code == 'LOCATION_FAILURE') {
// Weak/indoor signal — fall back to the cached fix before giving up.
return await tl.Tracelet.getLastKnownLocation();
}
rethrow;
}
}maximumAgedevuelve una corrección reciente en caché inmediatamente sin activar el GPS, ideal para asistencia/registro donde una posición de 30 s de antigüedad está bien.getLastKnownLocation()nunca activa un proveedor y devuelve lo que contiene el caché fusionado, por lo que solo muestra un mensaje de “señal débil” cuando realmente no hay nada disponible.- Mantenga
timeoutgeneroso (45–60 s) para la primera corrección después del lanzamiento y solicite a los usuarios que habiliten Precisión de ubicación de Google si las correcciones internas siguen fallando (detecta el estado del proveedor a través degetProviderState()/getHealth()).
Antecedentes y terminación
¿Tracelet sigue rastreando después de cerrar o borrar la aplicación?
Android: sí, con stopOnTerminate: false. Cuando el usuario elimina la aplicación de las recientes, Tracelet transfiere el seguimiento a un servicio nativo en segundo plano que no necesita motor Flutter, por lo que la captura y sincronización de la ubicación continúan. La notificación persistente del servicio en primer plano es lo que mantiene vivo ese servicio. Con stopOnTerminate: true, el seguimiento se detiene al deslizar el dedo como se esperaba.
iOS: depende de cómo se cerró la aplicación. Si iOS finaliza la aplicación por motivos de memoria/sistema, la reinicia en segundo plano en el siguiente cambio significativo de ubicación y la reanuda. Si el usuario fuerza el cierre de la aplicación (deslice el dedo hacia arriba en el selector de aplicaciones), iOS suspende deliberadamente todos sus servicios de ubicación hasta que la aplicación se abre nuevamente; Apple no permite que ningún SDK anule esto.
Configuré showNotificationOnPauseOnly: true pero la notificación siempre está visible. ¿Por qué?
Porque stopOnTerminate es false y las dos configuraciones no se pueden respetar al mismo tiempo.
Ocultar la notificación significa degradar el servicio en primer plano (Android no tiene ningún servicio en primer plano sin una notificación) y un proceso que no contiene ningún servicio en primer plano es el que Android elimina cuando el usuario elimina la aplicación de las recientes. Entonces, aunque la notificación estaba oculta, stopOnTerminate: false no garantizaba nada: un deslizamiento cronometrado dentro del espacio (medido en 285 ms en un Pixel Fold, 700-1500 ms en un dispositivo con Android 15) finalizó el proceso por completo, sin tareas sin cabeza, sin eventos ni registros. Nada de lo que hace un SDK en el momento de la eliminación de la tarea puede evitarlo: Android ya ha elegido qué procesos eliminar antes de informar a su aplicación que la tarea fue eliminada.
Tracelet lo resuelve a favor de la promesa de supervivencia: con stopOnTerminate: false se ignora la bandera y la notificación permanece activa. Registra el motivo una vez por start() en el canal del ciclo de vida, por lo que Tracelet.getLogs() lo mostrará.
Configure stopOnTerminate: true si prefiere tener la notificación oculta y desea que el seguimiento finalice cuando se elimina la aplicación. Explicación completa 📖
Sin conexión y sincronización
¿Qué pasa con mis ubicaciones si el dispositivo no tiene internet?
No se pierde nada. Cada corrección se escribe en la base de datos del dispositivo (cifrada cuando encryptDatabase: true) en el instante en que se captura, independientemente de la red. La sincronización automática los carga cuando vuelve la conectividad, reintentando con un retroceso exponencial (maxRetries, retryBackoffBase, retryBackoffCap). Un lote se elimina de la base de datos solo después de que el servidor confirma la recepción, por lo que una carga fallida o interrumpida simplemente se reintenta, nunca se descarta.
Las ubicaciones se encuentran dentro de sus límites de retención (maxDaysToPersist, de forma predeterminada, 3 días, y maxRecordsToPersist, ilimitado de forma predeterminada); los más viejos se podan una vez superados estos. Configure cualquiera de los dos en -1 para desactivar ese límite; vale la pena hacerlo deliberadamente si espera períodos sin conexión más largos que la ventana predeterminada, ya que un registro eliminado por retención nunca se cargó. Antes de la versión 3.8.3, en realidad no se aplicaba ningún límite, por lo que la cola crecía sin límites. También puedes retener cargas desde el celular con disableAutoSyncOnCellular: true.
¿Tracelet_sync se enviará automáticamente a mi backend cuando vuelva Internet?
Sí, automáticamente y completamente en segundo plano. Si usa tracelet_sync (o sus contenedores como tracelet_supabase / tracelet_firebase), no necesita escribir ninguna lógica de reintento de red usted mismo.
Cuando el sistema operativo detecta que se ha restaurado la conectividad de la red, el motor de sincronización nativo se activa inmediatamente en segundo plano y comienza a cargar las ubicaciones almacenadas en caché en su servidor en lotes cronológicos. Continúa cargándose hasta que la base de datos local esté completamente al día con el servidor, lo que garantiza cero pérdida de datos incluso después de períodos prolongados sin conexión.
Reiniciar y desbloquear dispositivo
Después de reiniciar, ¿Tracelet comienza a rastrear antes de que desbloquee el dispositivo?
No: el dispositivo debe desbloquearse al menos una vez después de reiniciar antes de que se reanude Tracelet. Esta es una regla de la plataforma Android (arranque directo/cifrado basado en archivos), no una limitación de Tracelet, y se aplica a cada SDK de ubicación.
Después de un arranque en frío, el dispositivo está en modo Arranque directo y la mayoría de los datos de la aplicación aún están cifrados. Android solo ofrece la transmisión BOOT_COMPLETED que escucha el receptor de arranque de Tracelet después de que el usuario desbloquea el dispositivo por primera vez (PIN/patrón/contraseña/biométrico). Hasta entonces, el almacenamiento cifrado de credenciales que contiene la base de datos de configuración, estado y ubicación de Tracelet es inaccesible: no hay nada que leer ni escribir.
Por lo tanto, la secuencia después de un reinicio es:
- El dispositivo arranca → Tracelet está inactivo.
- El usuario se desbloquea una vez →
BOOT_COMPLETEDse activa → Tracelet reanuda el seguimiento y la sincronización.
Sólo importa el primer desbloqueo. Después de eso, la pantalla se puede bloquear nuevamente (teléfono en el bolsillo, pantalla apagada) y el seguimiento/sincronización continúa normalmente.
Requisitos para reanudar el arranque: startOnBoot: true, stopOnTerminate: false y permiso de ubicación en segundo plano (“Siempre”) concedidos. En Android 14+, el sistema operativo además prohíbe iniciar un servicio en primer plano de ubicación desde el inicio, por lo que Tracelet recurre a WorkManager/seguimiento de alarmas (sin notificación persistente) hasta que se abre la aplicación la próxima vez.
iOS no puede iniciarse automáticamente al reiniciar: las aplicaciones no pueden ejecutarse al iniciarse y permanecer sin iniciarse hasta que el usuario abra la aplicación o un cambio significativo de ubicación la reinicie, lo que a su vez solo ocurre después del primer desbloqueo posterior al reinicio.
¿Puede realizar un seguimiento antes del primer desbloqueo?
No por defecto. Capturar ubicaciones antes del desbloqueo requiere un arranque directo de Android, lo que significa mover los datos que Tracelet necesita a un almacenamiento cifrado por dispositivo, legible antes de que el usuario se autentique, una garantía en reposo más débil que el almacenamiento cifrado con credenciales (y opcionalmente protegido con encryptDatabase) que Tracelet usa normalmente. Para mantener sus datos completamente protegidos, Tracelet no habilita el arranque directo de fábrica. Si el seguimiento previo al desbloqueo es un requisito estricto para su caso de uso, se puede habilitar como una opción avanzada a nivel de aplicación; comuníquese con nosotros antes de confiar en él.
Geocerca
Las transiciones de geocerca no se activan cuando pruebo con ubicaciones simuladas/simuladas (funcionó en 1.x)
Las ubicaciones simuladas aún funcionan, pero en el modo de geocerca de alta precisión, las correcciones simuladas se filtran antes de que se evalúe la geocerca. Con geofenceModeHighAccuracy: true, las transiciones se calculan en la aplicación a partir del flujo continuo de GPS y el evaluador solo ejecuta las correcciones que pasan el filtro de ubicación. Las herramientas de simulación de rutas suelen “teletransportarse” entre puntos y esos saltos son rechazados por:
maxImpliedSpeed: la velocidad inverosímil entre dos muestras muy alejadas se trata como un valor atípico y se elimina.useKalmanFilter: true: las peleas más suaves luchan contra saltos simulados abruptos y no físicos.trackingAccuracyThreshold: algunos proveedores simulados informanaccuracy = 0o un valor poco realista que no supera el umbral.
Una geocerca colocada en su ubicación actual aún se activa porque geofenceInitialTriggerEntry: true emite un ENTER al registrarse (no se necesita movimiento), por lo que nunca pasa por el filtro.
Para pruebas simuladas en modo de alta precisión, relaje los filtros:
geo: tl.GeoConfig(
distanceFilter: 0,
disableElasticity: true,
filter: tl.LocationFilter(
rejectMockLocations: false,
useKalmanFilter: false, // disable smoothing
maxImpliedSpeed: 0, // 0 disables the implied-speed reject
trackingAccuracyThreshold: 0, // accept regardless of reported accuracy
),
),Seleccione también su aplicación simulada en Opciones de desarrollador → Seleccionar aplicación de ubicación simulada y avance por la ruta simulada en pasos pequeños y realistas.
La ruta más sencilla es realizar la prueba en el modo de geocerca estándar (geofenceModeHighAccuracy: false), que delega al servicio de geocerca del sistema operativo. El sistema operativo evalúa el proveedor fusionado y respeta la aplicación de ubicación simulada del sistema sin los filtros SDK; use un radio de ≥ ~100 m allí, ya que el sistema operativo impone un mínimo práctico y las transiciones pequeñas/SALIDA no son confiables por debajo de eso.
Con debug: true y logLevel: verbose, observe los registros de Location filtered by Rust processor: <reason>: le indica exactamente qué filtro eliminó cada solución simulada.
¿Por qué veo ENTER/EXIT repetido para la misma geocerca, especialmente en teléfonos Android OEM agresivos (Xiaomi, Vivo, Oppo…)?
Tracelet ya persiste en un estado “conocido interno” seguro para reanudar que sobrevive a la muerte del proceso, por lo que un OEM que elimina y reinicia su aplicación no vuelve a activar ENTER para un dispositivo que nunca abandonó la zona.
La causa habitual es que la aplicación host llama a removeGeofences() inmediatamente antes de addGeofence() en cada inicio para “actualizar” una zona:
// Don't do this on every app start:
await tl.Tracelet.removeGeofences();
await tl.Tracelet.addGeofence(tl.Geofence(identifier: 'OFFICE', latitude: lat, longitude: lng, radius: radius));removeGeofences() borra deliberadamente ese estado interno persistente; tiene que hacerlo, ya que la valla realmente ha desaparecido. Dado que addGeofence() ya se actualiza por identificador, llamarlo nuevamente con nuevas coordenadas actualiza la valla existente en su lugar:
// Do this instead — updates OFFICE if it exists, creates it if not:
await tl.Tracelet.addGeofence(tl.Geofence(identifier: 'OFFICE', latitude: lat, longitude: lng, radius: radius));Sólo llame a removeGeofence() / removeGeofences() cuando realmente se esté eliminando una valla, no para actualizar una que todavía existe con el mismo identificador. En un dispositivo que rara vez se desactiva, la ruta de inicio quitar y luego agregar se ejecuta una vez durante el arranque en frío y es invisible. En un OEM agresivo, puede volver a ejecutarse docenas de veces por hora mientras el sistema operativo cierra y reinicia repetidamente su aplicación, y cada ejecución borra el estado interno justo antes de que llegue una solución GPS real, lo que produce cada vez un ENTER nuevo y técnicamente correcto desde la perspectiva ahora en blanco del SDK.
Seguridad y privacidad de los datos
¿Mis datos de ubicación están cifrados en reposo?
Opcionalmente si. Configure encryptDatabase: true para cifrar la base de datos SQLite local que almacena las ubicaciones. En Android, esto usa SQLCipher (AES-256) y requiere que agregues la dependencia de SQLCipher a tu aplicación; se mantiene opcional, por lo que la compilación predeterminada permanece pequeña y llamar a encryptDatabase sin ella genera un error claro. En iOS, el almacén cifrado se gestiona de forma nativa.
Para evidencia de manipulación en lugar de confidencialidad, el pista de auditoría (audit.enabled) encadena cada registro (por ejemplo, SHA-256) para que pueda demostrar que el historial no se modificó después del hecho. Zonas de privacidad te permiten suprimir o redactar correcciones dentro de áreas sensibles (como la casa de un usuario).
¿Tracelet detecta ubicaciones GPS simuladas o falsas?
Sí, a través de mockDetectionLevel en LocationFilter:
disabled(predeterminado): todas las ubicaciones se aceptan incondicionalmente.basic: confía en el indicador “es simulado” de la plataforma.heuristic: la bandera de la plataforma más heurísticas nativas y una verificación de marca de tiempo del lado de Dart, para detectar falsificadores que ocultan la bandera.
En heuristic, cada Location está anotado con el motivo por el que se consideró real o falso, por lo que puede aceptar, marcar o rechazar correcciones simuladas en su propia lógica.
Migrando desde flutter_background_geolocation
Vengo de flutter_background_geolocation. ¿Qué tan difícil es el cambio?
La API de Tracelet está intencionalmente cerca de flutter_background_geolocation, por lo que la mayoría de las aplicaciones se asignan con cambios mínimos: ready/start/stop, los eventos de ubicación/movimiento/proveedor y la sincronización HTTP tienen equivalentes directos. Consulte la guía de migración completa para ver la tabla de configuración/mapeo de eventos y algunas diferencias de comportamiento a tener en cuenta.