Skip to Content
Conceptos básicosEstado del servicio en primer plano

Estado del servicio en primer plano

En Android, un servicio de primer plano persistente es lo que mantiene la ubicación en segundo plano. Seguimiento vivo. Pero pedir rastrear (Tracelet.start()) no es lo mismo que el sistema operativo otorga un servicio en primer plano en ejecución. En Android 12+ un El inicio del servicio en primer plano puede ser diferido o rechazado, incluso mientras su aplicación cree que el seguimiento está habilitado.

Tracelet.getForegroundServiceHealth() cierra esa brecha. Informa el estado nativo autorizado del servicio en primer plano para que pueda identificar el diferencia entre “seguimiento solicitado” y “seguimiento en realidad corriendo” — y reaccionan cuando divergen.

Nuevo en 3.6.6. Esto complementa las correcciones de confiabilidad del servicio en primer plano en 3.6.5 (#253 ) y 3.6.6 (#254 ) haciendo que el servicio Estado real observable desde Dart.


Por qué enabled no es suficiente

Tracelet.getState().enabled es el estado deseado: la intención persistente de pista. Responde “¿la aplicación ha solicitado realizar un seguimiento?”, no “¿es realmente el sistema operativo? ejecutando el servicio de primer plano ahora mismo?”.

Estos pueden diferir, especialmente en Android 12+ (API 31+), donde iniciar un El servicio location en primer plano desde segundo plano está restringido:

  • El inicio se puede diferir: Android lo rechaza mientras la aplicación está en segundo plano y Tracelet lo vuelve a intentar automáticamente la próxima vez que la aplicación vuelva a el primer plano.
  • El inicio puede fallar por completo, p. un permiso faltante, o un NO TRADUCIR.
  • El sistema puede promocionar y luego detener el servicio.

En todos estos casos, enabled permanece true, pero el seguimiento en segundo plano no operacional. Un organismo de control de la marca de tiempo y ubicación puede eventualmente notar estancamiento, pero no puede decirle por qué: si la promoción falló, el servicio se detuvo o el proveedor simplemente está esperando una nueva solución. getForegroundServiceHealth() le da la razón real y autorizada.


La API

final health = await Tracelet.getForegroundServiceHealth();

Devuelve un Map<String, Object?> con las siguientes claves:

ClaveTipoSignificado
desiredEnabledboolEl estado de seguimiento persistente deseado (igual que getState().enabled).
foregroundServiceEnabledboolSi la configuración activa ejecuta un servicio en primer plano.
serviceRunningboolSi el proceso del servicio de ubicación nativa está activo.
serviceForegroundboolSi el servicio está actualmente promovido al primer plano (el último startForeground() tuvo éxito y no ha sido degradado/detenido desde entonces).
foregroundNotificationIdint?La identificación de la notificación durante la promoción; null en caso contrario.
lastForegroundPromotionResultString?success, deferred o failed: el resultado del intento de promoción más reciente (null antes de cualquier intento).
lastForegroundPromotionFailureClassString?Clase de excepción de la última promoción fallida/diferida (por ejemplo, ForegroundServiceStartNotAllowedException).
lastForegroundPromotionFailureMessageString?El mensaje de excepción.
lastForegroundTransitionAtint?Época: milisegundos de la última transición de ascenso.
platformStringandroid, ios o web.

La forma del mapa es intencionalmente la misma en todas las plataformas, por lo que el código multiplataforma puede leerlo uniformemente. Sólo los valores difieren según la plataforma (ver más abajo).


Leyendo el resultado de la promoción

serviceForeground combinado con lastForegroundPromotionResult le indica el historia completa:

desiredEnabledserviceForegroundlastForegroundPromotionResultInterpretación
falsefalsecualquieraEl seguimiento está desactivado, no hay nada de qué preocuparse.
truetruesuccessSaludable: el servicio en primer plano está en funcionamiento y promocionado.
truefalsedeferredDiferido: Android rechazó el inicio mientras estaba en segundo plano; Tracelet volverá a intentarlo cuando la aplicación vuelva al primer plano.
truefalsefailedFalló: la promoción falló; El seguimiento en segundo plano no está operativo. Inspeccione la clase/mensaje de falla.
truefalsenull⌛ Solicitado pero aún no confirmado (transitorio; encuesta nuevamente en breve).

Un indicador de seguimiento de la salud

El uso más común: mostrar un estado honesto al usuario (o su telemetría) en lugar de confiar ciegamente en enabled.

Future<String> describeTrackingHealth() async { final h = await Tracelet.getForegroundServiceHealth(); if (h['desiredEnabled'] != true) return 'Tracking off'; // iOS/web have no foreground service — enabled tracking is as good as it gets. if (h['platform'] != 'android') return 'Tracking active'; if (h['serviceForeground'] == true) return 'Tracking active'; switch (h['lastForegroundPromotionResult']) { case 'deferred': return 'Waiting to start — reopen the app to resume background tracking'; case 'failed': final reason = h['lastForegroundPromotionFailureMessage'] ?? 'unknown'; return 'Background tracking failed to start: $reason'; default: return 'Starting…'; } }

Un perro guardián de la recuperación

Empareje la verificación de estado con un temporizador periódico para detectar y recuperarse de una falla. promoción: por ejemplo, solicitar al usuario que vuelva a abrir la aplicación o volver a solicitar la permiso faltante.

Timer.periodic(const Duration(minutes: 1), (_) async { final h = await Tracelet.getForegroundServiceHealth(); final desired = h['desiredEnabled'] == true; final foreground = h['serviceForeground'] == true; final result = h['lastForegroundPromotionResult']; if (desired && !foreground && result == 'failed') { // Background tracking is not operational. Log it, alert your backend, // or guide the user to fix permissions / battery settings. await reportTrackingDegraded( failureClass: h['lastForegroundPromotionFailureClass'], failureMessage: h['lastForegroundPromotionFailureMessage'], ); } });

serviceForeground refleja el resultado de la última promoción, no una encuesta en vivo de el sistema operativo cada milisegundo. Inmediatamente después de start(), la promoción comienza a funcionar. más tarde: vuelva a sondear después de un breve retraso (1 a 2 s) si necesita el valor más reciente.


Comportamiento de la plataforma

PlataformaComportamiento
AndroidCompletamente completo con el estado del servicio en primer plano y el historial de promociones en vivo.
iOSNo hay ningún servicio en primer plano que pueda fallar después del hecho, por lo que serviceForeground es false, foregroundServiceEnabled es false y los campos de promoción son null. desiredEnabled y serviceRunning reflejan si el seguimiento está activo. platform es ios.
WebSin servicio en primer plano. Devuelve un mapa mínimo que refleja el estado deseado; platform es web.

Esta es la razón por la que iOS no se ve afectado por la carrera por la promoción de servicios de primer plano que se desarrolló. arreglado para Android en #253  y #254 : iOS no tiene proceso de servicio en primer plano que puede promoverse y luego derribarse.


En Tracelet Doctor

No es necesario que construyas nada de esto tú mismo para verlo. El La superposición tracelet_doctor ahora incluye un Primer plano Tarjeta de servicio que muestra exactamente esta información: deseada versus real, resultado de la promoción y cualquier clase/mensaje de error, con un estado codificado por colores (Saludable / Diferido / Fallido / Inactivo).

tracelet_doctor es una dependencia de desarrollo (flutter pub add dev:tracelet_doctor), por lo que guárdalo con kDebugMode:

import 'package:flutter/foundation.dart' show kDebugMode; import 'package:tracelet_doctor/tracelet_doctor.dart'; if (kDebugMode) { TraceletDoctor.show(context); // includes the Foreground Service card }

Los mismos campos también se capturan en el informe de error (TraceletBugReport.build()), por lo que un informe pegado muestra si el servicio en primer plano realmente se estaba ejecutando en el momento del problema: a menudo la pista que falta en “el seguimiento se detiene en el informes de antecedentes”.


Es bueno saberlo

  • Solo lectura y económico. La llamada solo lee el estado nativo en memoria; nunca inicia, detiene o modifica el seguimiento.
  • Seguro antes de ready(). Devuelve una instantánea predeterminada sensata en lugar de arrojando si el SDK no se ha inicializado.
  • Lo deseado versus lo real es el punto. Continúe usando getState().enabled para la intención de su aplicación; use getForegroundServiceHealth() para verificar que el sistema operativo esté honrando esa intención.
Last updated on