フォアグラウンドサービスの健全性
Android では、永続的なフォアグラウンド サービスがバックグラウンドで位置情報を保持します。
追跡は生きています。ただし、追跡を依頼する (Tracelet.start()) ことは、
OS がフォアグラウンド サービスの実行を 許可します。 Android 12 以降では
アプリの実行中であっても、フォアグラウンド サービスの開始は 遅延 または 拒否 される可能性があります
追跡が有効になっていると考えられます。
Tracelet.getForegroundServiceHealth() はそのギャップを埋めます。それは、
フォアグラウンド サービスの権限のあるネイティブ状態。
「追跡が要求されました」 と 「追跡は実際に行われます」の違い
running” — それらが発散したときに反応します。
enabled では不十分な理由
Tracelet.getState().enabled は 望ましい 状態です。つまり、永続的な意図です。
追跡。 「アプリは追跡を要求しましたか?」 ではなく、実際に OS であると答えます。
今フォアグラウンド サービスを実行していますか?”*。
特に Android 12 以降 (API 31 以降) では異なる場合があります。
バックグラウンドからの location フォアグラウンド サービスは制限されています。
- 開始は 延期できます — アプリの実行中は Android が開始を拒否します。 アプリが次にバックグラウンドに戻ったときに、Tracelet が自動的に再試行します。 前景。
- 開始は完全に失敗する可能性があります — 例:権限が不足している、または 翻訳されません。
- サービスはシステムによって プロモートされてから停止できます。
これらすべてのケースで、enabled は true のままですが、バックグラウンド追跡はありません
稼働中。ロケーションタイムスタンプウォッチドッグは最終的には古いことに気づく可能性がありますが、
理由 はわかりません。プロモーションが失敗したか、サービスが停止したか、または
プロバイダーは新しい修正を待っているだけです。非トランスレート
実際の権威ある理由を示します。
API
final health = await Tracelet.getForegroundServiceHealth();次のキーを含む Map<String, Object?> を返します。
| キー | タイプ | 意味 |
|---|---|---|
| 非トランスレート | 翻訳1遅れ | 永続化された 望ましい 追跡状態 (getState().enabled と同じ)。 |
| 非トランスレート | 翻訳1遅れ | アクティブな構成がフォアグラウンド サービスを実行しているかどうか。 |
| 非トランスレート | 翻訳1遅れ | ネイティブ位置情報サービスプロセスが稼働しているかどうか。 |
| 非トランスレート | 翻訳1遅れ | サービスが現在フォアグラウンドに昇格しているかどうか (最後の startForeground() が成功し、それ以降降格/停止されていない)。 |
| 非トランスレート | 翻訳1遅れ | 昇格中の通知 ID。それ以外の場合は null。 |
| 非トランスレート | 翻訳1遅れ | success、deferred、または failed — 最新のプロモーション試行の結果 (試行前の null)。 |
| 非トランスレート | 翻訳1遅れ | 最後に失敗または延期されたプロモーションの例外クラス (例: ForegroundServiceStartNotAllowedException)。 |
| 非トランスレート | 翻訳1遅れ | 例外メッセージ。 |
| 非トランスレート | 翻訳1遅れ | 最後のプロモーション遷移のエポックミリ秒。 |
| 非トランスレート | 翻訳1遅れ | android、ios、または web。 |
マップの形状はすべてのプラットフォームで意図的に同じであるため、クロスプラットフォーム コード 均一に読み取れます。 値 のみがプラットフォームごとに異なります (下記を参照)。
昇格結果の見方
serviceForeground と lastForegroundPromotionResult を組み合わせると、
全話:
| 非トランスレート | 翻訳1遅れ | lastForegroundPromotionResult | 解釈 |
|---|---|---|---|
| 非トランスレート | 翻訳1遅れ | 任意 | 追跡はオフになっていますので、心配する必要はありません。 |
| 非トランスレート | 翻訳1遅れ | success | ✅ 正常 — フォアグラウンド サービスは実行されており、昇格されています。 |
| 非トランスレート | 翻訳1遅れ | deferred | ⏳ 延期 — Android はバックグラウンドでの起動を拒否しました。アプリがフォアグラウンドに戻ったときに、Tracelet が再試行します。 |
| 非トランスレート | 翻訳1遅れ | failed | ❌ 失敗 — 昇格は失敗しました。バックグラウンド追跡は機能しません。失敗クラス/メッセージを検査します。 |
| 非トランスレート | 翻訳1遅れ | null | ⌛ リクエストされましたが、まだ確認されていません (一時的 - すぐに再度ポーリングします)。 |
追跡健全性インジケーター
最も一般的な用途: 正直なステータスをユーザー (またはテレメトリ) に明らかにする
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 は、ライブ投票ではなく、最後のプロモーション結果を反映します。
OS をミリ秒ごとに監視します。 start()の直後にプロモーションが始まります
後で — 最新の値が必要な場合は、短い遅延 (1 ~ 2 秒) 後に再度ポーリングします。
プラットフォームの動作
| プラットフォーム | 行動 |
|---|---|
| アンドロイド | ライブのフォアグラウンド サービスの状態とプロモーション履歴が完全に入力されます。 |
| iOS | 事後に失敗する可能性のあるフォアグラウンド サービスはないため、serviceForeground は false、foregroundServiceEnabled は false、プロモーション フィールドは null になります。 desiredEnabled と serviceRunning は、追跡がアクティブかどうかを反映します。 platformはiosです。 |
| ウェブ | フォアグラウンドサービスはありません。目的の状態を反映する最小限のマップを返します。 platformはwebです。 |
Tracelet Doctor で
「見る」ためにこれを自分で構築する必要はありません。の
tracelet_doctor オーバーレイに Foreground が含まれるようになりました
まさにこの情報をレンダリングするサービス カード (望ましい情報と実際の情報、
昇格結果と失敗クラス/メッセージ — 色分けされたステータス付き
(正常/遅延/失敗/非アクティブ)。
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()を使用して OS が その意図を尊重します。