Skip to Content
Основные понятияСостояние службы переднего плана

Состояние службы переднего плана

В Android постоянная служба переднего плана — это то, что сохраняет фоновое местоположение. отслеживание живое. Но просить отслеживать (Tracelet.start()) — это не то же самое, что ОС предоставляет работающую службу переднего плана. На Android 12+ а Запуск службы переднего плана может быть отложен или отклонён — даже если ваше приложение считает, что отслеживание включено.

Tracelet.getForegroundServiceHealth() закрывает этот пробел. В нем сообщается авторитетное собственное состояние службы переднего плана, чтобы вы могли сообщить разница между “отслеживание было запрошено” и “отслеживание на самом деле бегут» — и реагируют, когда они расходятся.

Новое в версии 3.6.6. Это дополняет исправления надежности приоритетных служб в 3.6.5 (#253 ) и 3.6.6 (#254 ) путем создания службы фактическое состояние, наблюдаемое из Dart.


Почему 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.

Вот почему на iOS не влияет гонка за продвижение приоритетных сервисов, которая была исправлено для Android в #253  и #254 : iOS не имеет отдельного процесс приоритетного обслуживания, который можно повысить, а затем свернуть.


В Траселет Доктор

Вам не обязательно создавать что-либо из этого самостоятельно, чтобы увидеть это. Наложение 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(), чтобы убедиться, что ОС уважая это намерение.
Last updated on