前台服务健康状况
在 Android 上,持久的前台服务可以保持后台位置
活着追踪。但是 要求 跟踪 (Tracelet.start()) 与
操作系统授予正在运行的前台服务。在 Android 12+ 上
前台服务启动可以“推迟”或“拒绝”——即使您的应用程序
认为跟踪已启用。
Tracelet.getForegroundServiceHealth() 缩小了这一差距。它报告了
前台服务的权威本机状态,以便您可以告诉
“请求跟踪”和“跟踪实际上是
运行”——当它们出现分歧时做出反应。
为什么 enabled 还不够
Tracelet.getState().enabled 是 所需 状态 — 持续的意图
追踪。它回答*“应用程序是否要求跟踪?”,而不是“操作系统实际上是
现在正在运行前台服务吗?”*.
这些可能会有所不同,特别是在 Android 12+ (API 31+) 上,在其中启动
location 前台服务从后台受到限制:
- 启动可以被推迟——Android 在应用程序启动时拒绝它 后台运行,当应用程序下次返回时 Tracelet 会自动重试 前景。
- 启动可能会彻底失败——例如缺少权限,或者 未翻译。
- 系统可以提升该服务,然后停止。
在所有这些情况下,enabled 保持 true,但后台跟踪不是
操作。位置时间戳看门狗最终可以注意到陈旧性,但是
它无法告诉您为什么——是否升级失败、服务被停止,或者
提供商只是在等待新的修复。不翻译迟到
给你真实、权威的理由。
应用程序编程接口
final health = await Tracelet.getForegroundServiceHealth();返回具有以下键的 Map<String, Object?>:
| 关键 | 类型 | 意义 |
|---|---|---|
| 不翻译迟到 | 不翻译晚点 | 持续的所需跟踪状态(与 getState().enabled 相同)。 |
| 不翻译迟到 | 不翻译晚点 | 活动配置是否运行前台服务。 |
| 不翻译迟到 | 不翻译晚点 | 本机位置服务进程是否存活。 |
| 不翻译迟到 | 不翻译晚点 | 服务是否当前提升到前台(上次 startForeground() 成功,并且此后尚未降级/停止)。 |
| 不翻译迟到 | 不翻译晚点 | 促销时的通知ID;否则null。 |
| 不翻译迟到 | 不翻译晚点 | success、deferred 或 failed — 最近一次升级尝试的结果(任何尝试之前的 null)。 |
| 不翻译迟到 | 不翻译晚点 | 上次失败/延迟升级的异常类(例如 ForegroundServiceStartNotAllowedException)。 |
| 不翻译迟到 | 不翻译晚点 | 异常消息。 |
| 不翻译迟到 | 不翻译晚点 | 上次升级转换的纪元毫秒数。 |
| 不翻译迟到 | 不翻译晚点 | android、ios 或 NOTTRANS4LATE。 |
地图形状在每个平台上都故意相同,因此跨平台代码 可以统一读取。每个平台仅值有所不同(见下文)。
解读促销结果
serviceForeground 与 lastForegroundPromotionResult 结合告诉您
整个故事:
| 不翻译迟到 | 不翻译晚点 | 不翻译2晚 | 解读 |
|---|---|---|---|
| 不翻译迟到 | 不翻译晚点 | 任何 | 跟踪已关闭 — 无需担心。 |
| 不翻译迟到 | 不翻译晚点 | 不翻译2晚 | ✅ 健康 — 前台服务正在运行并提升。 |
| 不翻译迟到 | 不翻译晚点 | 不翻译2晚 | ⏳ 延迟 — Android 在后台拒绝启动;当应用程序返回前台时,Tracelet 将重试。 |
| 不翻译迟到 | 不翻译晚点 | 不翻译2晚 | ❌ 失败——晋升失败;后台跟踪不运行。检查故障类别/消息。 |
| 不翻译迟到 | 不翻译晚点 | 不翻译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 反映跟踪是否处于活动状态。 NOTTRANS7LATE 是 NOTTRANS8LATE。 |
| 网络 | 无前台服务。返回反映所需状态的最小映射; NOTTRANS0LATE 是 NOTTRANS1LATE。 |
在 Tracelet 医生中
您不必自己构建任何这些来“看到”它。这
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()验证操作系统是否 尊重这一意图。