Состояние службы переднего плана
В Android постоянная служба переднего плана — это то, что сохраняет фоновое местоположение.
отслеживание живое. Но просить отслеживать (Tracelet.start()) — это не то же самое, что
ОС предоставляет работающую службу переднего плана. На Android 12+ а
Запуск службы переднего плана может быть отложен или отклонён — даже если ваше приложение
считает, что отслеживание включено.
Tracelet.getForegroundServiceHealth() закрывает этот пробел. В нем сообщается
авторитетное собственное состояние службы переднего плана, чтобы вы могли сообщить
разница между “отслеживание было запрошено” и “отслеживание на самом деле
бегут» — и реагируют, когда они расходятся.
Почему enabled недостаточно
Tracelet.getState().enabled — это желаемое состояние — постоянное намерение
отслеживать. Он отвечает “приложение попросило отслеживать?”, а не “на самом деле ОС
прямо сейчас запускаете службу переднего плана?”.
Они могут отличаться, особенно на Android 12+ (API 31+), где запуск
Служба переднего плана location в фоновом режиме ограничена:
- Запуск можно отложить — Android отказывается от этого, пока приложение работает. в фоновом режиме, и Tracelet автоматически повторяет попытку, когда приложение в следующий раз возвращается в передний план.
- Старт может сразу провалиться — например. отсутствующее разрешение или НЕ ПЕРЕВОДИТЬ.
- Система может продвигать услугу, а затем останавливать.
Во всех этих случаях enabled остается true, но фоновое отслеживание нет.
оперативный. В конечном итоге наблюдатель за местоположением и временными метками может заметить устаревшие данные, но
он не может сказать вам почему — не удалось ли продвижение, служба была остановлена или
провайдер просто ждет свежего исправления. getForegroundServiceHealth()
дает вам реальную, авторитетную причину.
API
final health = await Tracelet.getForegroundServiceHealth();Возвращает Map<String, Object?> со следующими ключами:
| Ключ | Тип | Значение |
|---|---|---|
| НОТРАНС0ЛАТЕ | НОТРАНС1ЛАТЕ | Постоянное желаемое состояние отслеживания (то же самое, что getState().enabled). |
| НОТРАНС0ЛАТЕ | НОТРАНС1ЛАТЕ | Запускает ли активная конфигурация вообще службу переднего плана. |
| НОТРАНС0ЛАТЕ | НОТРАНС1ЛАТЕ | Активен ли собственный процесс службы определения местоположения. |
| НОТРАНС0ЛАТЕ | НОТРАНС1ЛАТЕ | Выведена ли служба в настоящий момент на передний план (последний startForeground() завершился успешно, и с тех пор она не была понижена в должности/остановлена). |
| НОТРАНС0ЛАТЕ | НОТРАНС1ЛАТЕ | Идентификатор уведомления во время продвижения; null в противном случае. |
| НОТРАНС0ЛАТЕ | НОТРАНС1ЛАТЕ | success, deferred или failed — результат последней попытки продвижения (null перед любой попыткой). |
| НОТРАНС0ЛАТЕ | НОТРАНС1ЛАТЕ | Класс исключения последнего неудачного/отложенного продвижения (например, ForegroundServiceStartNotAllowedException). |
| НОТРАНС0ЛАТЕ | НОТРАНС1ЛАТЕ | Сообщение об исключении. |
| НОТРАНС0ЛАТЕ | НОТРАНС1ЛАТЕ | Эпоха-миллисекунды последнего перехода продвижения. |
| НОТРАНС0ЛАТЕ | НОТРАНС1ЛАТЕ | android, ios или web. |
Форма карты намеренно одинакова на всех платформах, поэтому код является кроссплатформенным. можно прочитать его равномерно. В зависимости от платформы различаются только значения (см. ниже).
Чтение результата продвижения
serviceForeground в сочетании с lastForegroundPromotionResult сообщает вам
вся история:
| НОТРАНС0ЛАТЕ | НОТРАНС1ЛАТЕ | НОТРАНС2ЛАТЕ | Интерпретация |
|---|---|---|---|
| НОТРАНС0ЛАТЕ | НОТРАНС1ЛАТЕ | любой | Отслеживание отключено — не о чем беспокоиться. |
| НОТРАНС0ЛАТЕ | НОТРАНС1ЛАТЕ | НОТРАНС2ЛАТЕ | ✅ Работно — служба переднего плана запущена и повышена. |
| НОТРАНС0ЛАТЕ | НОТРАНС1ЛАТЕ | НОТРАНС2ЛАТЕ | ⏳ Отложено — Android отказывался запускаться в фоновом режиме; Tracelet повторит попытку, когда приложение вернется на передний план. |
| НОТРАНС0ЛАТЕ | НОТРАНС1ЛАТЕ | НОТРАНС2ЛАТЕ | ❌ Failed — продвижение не удалось; Фоновое отслеживание не работает. Проверьте класс/сообщение об отказе. |
| НОТРАНС0ЛАТЕ | НОТРАНС1ЛАТЕ | НОТРАНС2ЛАТЕ | ⌛ Запрошено, но еще не подтверждено (временно — вскоре повторится опрос). |
Индикатор отслеживания работоспособности
Наиболее распространенное использование: предоставить пользователю (или вашей телеметрии) честный статус.
вместо того, чтобы слепо доверять 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…';
}
}Наблюдатель за восстановлением
Совместите проверку работоспособности с периодическим таймером для обнаружения и восстановления после сбоя. продвижение — например, предложите пользователю повторно открыть приложение или повторно запросите отсутствует разрешение.
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 отражает результаты последней промоакции, а не живой опрос
ОС каждую миллисекунду. Сразу после start() акция становится популярной.
позже — повторите опрос через небольшую задержку (1–2 с), если вам нужно самое свежее значение.
Поведение платформы
| Платформа | Поведение |
|---|---|
| Андроид | Полностью заполнен актуальной информацией о состоянии служб переднего плана и историей продвижения. |
| iOS | Не существует приоритетной службы, которая могла бы выйти из строя постфактум, поэтому serviceForeground — это false, foregroundServiceEnabled — это false, а поля повышения — null. desiredEnabled и serviceRunning отражают, активно ли отслеживание. platform — это ios. |
| Интернет | Никакого обслуживания на переднем плане. Возвращает минимальную карту, отражающую желаемое состояние; platform — это web. |
В Траселет Доктор
Вам не обязательно создавать что-либо из этого самостоятельно, чтобы увидеть это.
Наложение tracelet_doctor теперь включает в себя передний план.
Сервисная карта, которая отображает именно эту информацию — желаемую или фактическую,
результат продвижения и любой класс/сообщение об отказе — с цветовым кодом статуса
(Исправен/Отложен/Сбой/Неактивен).
tracelet_doctor — это зависимость (flutter pub add dev:tracelet_doctor), поэтому
защитите его с помощью 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
}Те же поля также фиксируются в отчете об ошибке (TraceletBugReport.build()),
поэтому вставленный отчет показывает, действительно ли служба переднего плана работала на
время возникновения проблемы — часто недостающий ключ к «отслеживанию остановок в
фон», сообщает.
Приятно знать
- Только чтение и дешево. Вызов просто считывает исходное состояние в памяти; это никогда запускает, останавливает или изменяет отслеживание.
- Безопасно до
ready(). Он возвращает разумный снимок по умолчанию, а не выдает ошибку, если SDK не был инициализирован. - Всё дело в желаемом и реальном. Продолжайте использовать
getState().enabledдля цель вашего приложения; используйтеgetForegroundServiceHealth(), чтобы убедиться, что ОС уважая это намерение.