地理围栏 API
Tracelet 提供了一个强大的、离线优先的地理围栏引擎。与限制为 20-100 个地理围栏并且需要操作系统唤醒您的应用程序的标准操作系统地理围栏不同,Tracelet 在 Rust 引擎内部评估地理围栏。这使得无限地理围栏具有零额外电池消耗。
1.圆形与多边形地理围栏
Tracelet 支持标准圆形地理围栏和复杂多边形地理围栏。
圆形地理围栏
圆形地理围栏由中心坐标和半径(以米为单位)定义。
final circular = tl.Geofence(
identifier: 'headquarters',
latitude: 37.7749,
longitude: -122.4194,
radius: 200, // 200 meters
notifyOnEntry: true,
notifyOnExit: true,
notifyOnDwell: true, // Triggers if they stay inside
loiteringDelay: 300, // Trigger dwell after 5 minutes
);多边形地理围栏
多边形地理围栏由勾勒出复杂形状(如公园或建筑物)的坐标数组定义。
final polygon = tl.Geofence.polygon(
identifier: 'golden_gate_park',
vertices: [
tl.Coordinate(latitude: 37.773972, longitude: -122.431297),
tl.Coordinate(latitude: 37.769996, longitude: -122.511055),
tl.Coordinate(latitude: 37.764516, longitude: -122.508566),
],
notifyOnEntry: true,
notifyOnExit: true,
);2. 添加和删除地理围栏
Tracelet 将所有地理围栏存储在本地 SQLite 数据库中。这意味着它们在应用程序重新启动后保持不变。您无需在每次应用程序启动时重新添加它们。
添加地理围栏
您可以添加单个地理围栏或一组地理围栏。
// Add a single geofence
await tl.Tracelet.addGeofence(circular);
// Add multiple geofences efficiently
await tl.Tracelet.addGeofences([circular, polygon]);移除地理围栏
通过其唯一标识符删除它们,或清除整个数据库。
// Remove a specific geofence
await tl.Tracelet.removeGeofence('headquarters');
// Remove all geofences entirely
await tl.Tracelet.removeGeofences();3. 列出活动地理围栏
您可以随时查询SQLite数据库,查看当前正在监控哪些地理围栏。
final geofences = await tl.Tracelet.getGeofences();
for (final fence in geofences) {
print('Monitoring: \${fence.identifier}');
}4. 监听地理围栏事件
当用户跨越地理围栏边界时,Tracelet 会触发 onGeofence 事件。由于评估发生在位置更新期间的 Rust 核心中,因此即使应用程序被终止,这些事件也会触发(触发无头回调)。
tl.Tracelet.onGeofence((evt) {
if (evt.action == tl.GeofenceEventAction.ENTER) {
print('User entered: \${evt.identifier}');
} else if (evt.action == tl.GeofenceEventAction.EXIT) {
print('User exited: \${evt.identifier}');
} else if (evt.action == tl.GeofenceEventAction.DWELL) {
print('User is dwelling inside: \${evt.identifier}');
}
});5. Android:前台服务和 Google Play 政策
Google Play 政策变更 — 2026 年 10 月 28 日生效。 地理围栏不适用
不再是前台服务位置的允许用例。使用的应用程序
仅用于地理围栏的前台服务必须删除
FOREGROUND_SERVICE_LOCATION(和 FOREGROUND_SERVICE,如果未使用的话)
合并清单中的权限。
Tracelet 的 标准 地理围栏模式使用本机 Android 地理围栏 API
(GeofencingClient),它会触发 ENTER/EXIT — 并将您的应用程序重新启动到您的
无头任务 - 当应用程序暂停或终止时,没有
前台服务。在标准模式下调用 startGeofences() 不会**启动
前台服务(从 3.5.x 开始),因此默认情况下是合规的。
激进的 OEM(三星/小米/华为/OnePlus/Oppo/Vivo): 截至 3.6.1,
每台设备都会遵循显式的 geofenceModeHighAccuracy: false。前
3.6.1 这些 OEM 默默地强制采用高精度模式 — 随之而来的是
前台服务及其持久通知 - 即使您设置 false,
这打破了上面兼容的标准模式路径。如果您的目标是这些设备,
使用 3.6.1 或更高版本。在激进的 OEM 上,SDK 现在会记录可靠性警告
(本地地理围栏传输可能会延迟)而不是覆盖您的配置。
如果您仅将 Tracelet 用于地理围栏
-
不启用前台服务:
await tl.Tracelet.ready(const tl.Config( android: tl.AndroidConfig( foregroundService: tl.ForegroundServiceConfig(enabled: false), ), geofence: tl.GeofenceConfig(geofenceModeHighAccuracy: false), )); await tl.Tracelet.startGeofences(); -
Tracelet SDK声明
FOREGROUND_SERVICE/FOREGROUND_SERVICE_LOCATION因其连续跟踪用例。如果你的应用程序从不连续 跟踪,使用清单合并将它们从合并清单中剥离 应用程序的android/app/src/main/AndroidManifest.xml中的remove规则:<manifest xmlns:android="http://schemas.android.com/apk/res/android" xmlns:tools="http://schemas.android.com/tools"> <uses-permission android:name="android.permission.FOREGROUND_SERVICE_LOCATION" tools:node="remove" /> <uses-permission android:name="android.permission.FOREGROUND_SERVICE" tools:node="remove" /> </manifest>
如果您使用连续跟踪和地理围栏
您可以保留 FOREGROUND_SERVICE_LOCATION 用于跟踪用例(例如
导航/车队跟踪),但前台服务不得用于
地理围栏。 Tracelet 已经处理了这个问题:FGS 运行以进行连续跟踪
(start()),并切换到仅地理围栏模式(标准中的 startGeofences())
模式)拆除 FGS。
高精度地理围栏模式 (geofenceModeHighAccuracy: true) 使用
连续 GPS 进行严格的应用内边界检测,确实运行
前台服务。这是连续定位——而不是政策中的“地理围栏”
sense - 因此仅当您的应用程序有单独的允许位置使用时才启用它
案件。对于大多数应用程序来说,标准模式是合规且省电的选择。
6. GPS漂移和错误的EXIT事件(高精度模式)
在高精度模式下,每次 GPS 定位都会在应用程序内评估转换。一个 静止在小地理围栏内的设备偶尔会收到 单一固定点会漂移到半径之外——即使它从未移动过。到 为了防止触发错误的 EXIT,Tracelet 的退出决定是 精确度感知:只有当整个 GPS 错误时,圆形地理围栏才会退出 圆圈清除栅栏:
EXIT fires when: distance - horizontalAccuracy > radius + buffer修复报告为 160 m,但 ±150 m 的精度最多仅为“10 m” 乐观,因此它被视为仍在内部并且没有 EXIT 触发。一个自信、 同距离精准定位正常退出。故意留下 ENTER 准确性不可知,因此到达仍然会立即触发。
权衡
由于退出阈值随着修复报告的准确性而增加,真实 出发时间大致是由于当前 GPS 的不确定性而延迟。对于 50 m 地理围栏 (70 m 出口门槛):
| 水平精度 | EXIT 触发于 ~ |
|---|---|
| ±8 m(良好,户外) | 78 m |
| ±20 m(典型值) | 90 米 |
| ±50 m(差) | 120 m |
| ±150 m(非常差) | 220 米 |
在信号良好的户外,这个可以忽略不计。 GPS 长期较差 在某些条件下(室内深处、城市峡谷),真正的出口可能会明显滞后。
调整它:geofenceExitAccuracyMax
如果您的用例更喜欢更快、更急切的 EXIT — 或者您想绑定
最坏情况延迟 — 设置 GeofenceConfig.geofenceExitAccuracyMax(以米为单位):
| 价值 | 行为 |
|---|---|
-1 (默认) | 全精度门控。最能抵抗漂移引起的错误退出;真正的退出可能会因当前 GPS 的不确定性而滞后。 |
| 不翻译迟到 | 门控已禁用。一旦报告点清除 radius + buffer,EXIT 就会触发 — 最快的退出,但单个漂移尖峰可能会产生错误的 EXIT。 |
| 不翻译迟到 | 夹钳。吸收高达 N 米的漂移,但绝对不会延迟真正的 EXIT 超过 ~N 米。对于混合条件下的小围栏来说是一个很好的中间立场(例如 20)。 |
await tl.Tracelet.ready(const tl.Config(
geofence: tl.GeofenceConfig(
geofenceModeHighAccuracy: true,
// Absorb drift up to 20 m, but keep genuine exits reasonably prompt.
geofenceExitAccuracyMax: 20,
),
));此门控仅适用于高精度应用内路径。标准中
(操作系统区域监控)模式操作系统决定退出,并且
geofenceExitAccuracyMax 没有效果。
确认退出:单个错误修复永远不会触发退出
仅当漂移修复对其不确定性“诚实”时,准确度门控才会有帮助
(报告的准确性很高)。有些设备则相反:它们发出
过于自信修复了在栅栏外数百米处着陆的问题
报告严格的准确性。由于报告的准确度很小,门控
无法看穿它 - 200 m − 1.7 m = 198 m 仍然清除阈值。
Tracelet 通过第二个补充防御来捕获这些:必须退出 在连续两次超出范围的修复中得到确认。推理是 身体上的——离开地理围栏是一个“持续”的变化,而过度自信的人 故障总是一个单一的修复,跳出并在下一个修复中返回 一:
fix 1: 200 m out → held (pending confirmation), no EXIT
fix 2: back inside → pending cleared, glitch absorbed真正的出发停留在外面,所以第二次修复证实了这一点并且准确无误 一次 EXIT 触发 — 仅延迟一次位置更新(高精度情况下约为 1 秒) 模式)。滞后是退出决策的“空间”一半,这是 时间一半;两人一起工作。
该功能默认开启,无需配置。这个决定存在于 Rust 中 core,因此 Android 和 iOS 的行为相同。 (与精度门控一样,它适用于 仅适用于高精度应用程序内路径 - 在标准操作系统区域监控模式下 操作系统拥有 EXIT 决定。)