Skip to Content
其他工具和适配器常见问题解答和故障排除

常见问题解答

一般权限和崩溃

如果我们不添加任何权限会发生什么?会崩溃吗?

不会,Tracelet 不会崩溃。 Tracelet 被设计为具有高度弹性。如果您尝试在未声明或请求强制位置权限的情况下调用 Tracelet.start(),Tracelet 将优雅地捕获 SecurityException,将详细错误记录到控制台,并向侦听器发出失败事件。应用程序本身将继续正常运行。但是,在获得许可之前,不会记录任何位置。

如果我们不启用运动(体力活动)权限,会发生什么情况?

不会,它不会崩溃。 如果未授予 ACTIVITY_RECOGNITION (Android) 或 Motion & Fitness (iOS) 权限,Tracelet 将自动回退到标准的基于距离的跟踪。 然而,电池消耗将显着增加。如果没有运动检测,当设备静止时,Tracelet 无法让 GPS 硬件进入睡眠状态。强烈建议为生产应用程序请求此权限,以确保最佳的电池寿命。

iOS 构建和设置

iOS 构建失败,并出现“未定义符号”Rust/UniFFI 错误(_ffi_tracelet_core_rustbuffer_free_uniffi_tracelet_core_checksum_method_*

启用 Flutter 的 Swift Package Manager 集成。 Tracelet 的 Rust 核心(TraceletCore.xcframework,公开 tracelet_ios 使用的 UniFFI 符号)通过 Swift Package Manager 链接。在旧版 CocoaPods-only 路径上,框架未链接到 tracelet_ios 目标,因此 Xcode 的链接器报告未定义的符号,例如 _ffi_tracelet_core_rustbuffer_free_uniffi_tracelet_core_checksum_method_*

修复它:

flutter config --enable-swift-package-manager flutter clean flutter pub get flutter run # or: flutter build ios

笔记:

  • Flutter CLI 或 IDE (VS Code) 构建/运行,这会驱动 SPM 感知构建,而不是打开 .xcworkspace 并直接从 Xcode 构建。
  • flutter clean + 单独使用新的 pod install 并不能解决这个问题——缺少的部分是链接 Rust 框架的 SPM,而不是过时的 Pod。

电池和运动传感器

步行时运动传感器拉动会消耗更多电池吗?

不,它实际上节省了电池。 硬件运动传感器(加速计/步检测器)每小时消耗的电池少于 0.1%。当手机放在桌子上时,Tracelet 使用这种超低功耗传感器完全关闭极其耗电的 GPS 芯片(每小时消耗 4-8% 的电量)。

当手机不动时,为什么我无法获得持续更新?

这是有意为之的 — 这是 Tracelet 提供的最大的电池节省功能。 当运动检测确定设备处于静止状态时,Tracelet 会关闭连续 GPS 并切换到低功耗模式(定期一次性修复或地理围栏监控)。一旦真正的运动恢复,连续跟踪就会自动唤醒。

如果您仍然希望在静止时定期获得“我在这里”位置,请设置 heartbeatInterval(秒)。停车时的小 GPS 漂移不会使里程表膨胀 - 比 odometerAccuracyThreshold(默认 50 m)更糟糕的修复被排除在距离之外。

如何进一步减少电池使用量?

Tracelet 在静止时已经使 GPS 休眠,但您有几个杠杆:

  • 电池预算 — 将 batteryBudgetPerHour 设置为 GeoConfig(例如,3.0 为 3%/小时)。然后,Tracelet 在运行时自动调整 distanceFilterdesiredAccuracy 以保持在该目标之下。
  • 唤醒锁释放 — 在 AndroidConfig 中设置 releaseWakelockWhenStationary: true,让 CPU 在用户静止时完全睡眠(需要 MotionDetectionMode.smart)。
  • 距离过滤器 — 较大的 distanceFilter(米)在移动时记录的修正较少。
  • 精度较低medium/lowdesiredAccuracyhigh/best 消耗更少的电量。
  • 定期模式 - 对于“它们大致在哪里”用例,定期一次性修复 (startPeriodic) 比连续跟踪便宜得多。
  • 保持授予运动许可 — 如果没有 ACTIVITY_RECOGNITION Tracelet 就无法积极地使 GPS 休眠。

motionDetectionMode 选项之间有什么区别?

motionDetectionMode 决定 Tracelet 如何检测运动以启动/停止 GPS:

  • accelerometer — 使用硬件运动传感器(以及允许的活动识别)。功耗最低,可在室内工作且无需 GPS 定位。
  • speed — 仅使用 GPS 速度。简单且可预测,但它需要 GPS 定位才能注意到您已经停止,因此反应速度较慢且耗电较多。
  • smart — 结合两者:如果加速计GPS 速度表明您正在移动,则它会保持连续跟踪,并且仅当两者*都同意您已停止时才会保持静止。对错误转换最稳健;推荐用于大多数应用程序。

定位服务和跟踪状态

如果用户在跟踪处于活动状态时关闭定位服务,会发生什么情况?

简短回答: Tracelet 不会停止、崩溃或拆除跟踪。它使跟踪会话保持武装状态,发出providerchange事件,以便您的应用程序可以做出反应,在位置关闭时不记录新位置(下面有两个特定于平台的例外情况),并在用户重新启用位置时自动恢复提供位置 - 在每种状态下(前台、后台、终止)。您不需要**需要再次调用 start()

这里的“位置服务关闭”是指操作系统级别的位置切换(Android:设置 → 位置;iOS:设置 → 隐私 → 位置服务)。撤销应用程序的权限是一个相关但独立的案例 - 请参阅本答案末尾的注释。

如何检测

final sub = tl.Tracelet.onProviderChange((tl.ProviderChangeEvent e) { if (!e.enabled) { // Location services were turned OFF — prompt the user / show a banner. } else { // Back ON — Tracelet has already resumed; no action required. } });

ProviderChangeEvent 包含 enabledstatus(授权)、gpsnetworkaccuracyAuthorizationgpsFallbackmockLocationsDetected。相同的事件会在后台/终止状态(如果已注册)下传递到 headless 回调,并作为 providerchange 记录保留,除非您设置 disableProviderChangeRecord: true

按国家和平台划分的行为

状态安卓iOS
Foregroundproviderchange (enabled: false) 火灾;前台服务保持活动状态;在重新启用之前没有新的修复。providerchange fires; didFailWithError 得到妥善处理(一次回落到最后一个已知位置); no new fixes.
背景前台服务(及其通知)保持运行;融合更新就会停止; providerchange 仍已发送。重新启用后恢复。CLLocationManager 订阅保持注册状态; iOS 不会提供任何信息,直到位置返回,然后恢复(并且可以通过区域/SLC 重新启动应用程序)。
终止(杀死)如果后台/启动会话处于活动状态 (stopOnTerminate: false / startOnBoot),则服务的行为与上面的 Background 相同。如果该进程不存在,则在操作系统下次启动它之前不会运行任何内容。iOS 会通过重要位置/区域监控重新启动应用程序仅当发生位置/区域事件时 - 当位置关闭时则无法启动应用程序。重新启用后,下一场排位赛将重新启动并继续。

在所有情况下,会话状态都会保留,因此重新启用位置会自动恢复跟踪,而无需重新初始化。

Tracelet 做什么

  • 不会自动停止会话或清除您的配置/状态。
  • 不会抛出或崩溃——捕获平台“位置关闭/拒绝”错误。
  • 不会制造位置(Android 航位推算除外,见下文)——您的数据库只是在位置关闭期间存在间隙。

值得了解的平台细节

Android — 如果用户禁用 GPS 但 Wi-Fi/小区定位仍然开启,Tracelet 会自动 回退 到平衡功率定位,并发出 providerchangegpsFallback: true,在 GPS 返回时恢复完全精度。这些近似修复**被记录并同步(标记为 locationSource 及其真实的 accuracy)。如果 enableDeadReckoning: true,则在 GPS 丢失配置的延迟后,Tracelet 会根据运动传感器估计位置,直到返回真正的定位。持久通知始终保持可见。

iOS — 失败的 requestLocation() 会使用最后一个已知位置解决一次性请求,而不是挂起。当位置关闭时,iOS 不提供后台或重新启动事件;一旦重新打开,交付和终止状态重新启动就会恢复。

你应该在你的应用程序中做什么

  1. 订阅 onProviderChange 并在 enabled == false 时显示横幅/对话框。
  2. (可选)引导用户通过 Tracelet.openLocationSettings() 进行设置。
  3. 重新启用时不要再次调用 start() — Tracelet 会自行恢复;调用 start() 无害但不必要。

撤销应用程序的权限与关闭开关

关闭切换会影响所有应用程序,并且可以如上所述完全恢复。撤销应用程序的位置权限(或降级“始终”→“使用时”)是通过 status / accuracyAuthorization 字段通过相同的 providerchange 事件报告的。在 Android 12+ 上,如果后台位置权限丢失,则会故意跳过启动/后台重新启动(否则会静默失败)——重新授予权限并重新初始化以恢复。

位置的准确度如何?如何区分 GPS 和 Wi-Fi/手机定位?

每个 Location 都带有一个以米为单位的真实 coords.accuracy 和一个 locationSource 标签:"gps" (≤50 m)、"wifi" (≤200 m)、"cell"(更差)或 "network"(GPS 回退期间)。 Tracelet 不会默默地放弃低精度修复 - 它会记录它们,以便您的轨迹保持连续 - 但它会将不良修复排除在 里程表 之外(odometerAccuracyThreshold,默认 50 m)并拒绝不可能的速度跳跃(maxImpliedSpeed)。

如果您只需要 GPS 质量的数据,请使用 locationSource == "gps"accuracy <= 50 进行过滤。如果用户仅授予近似/粗略位置(或 iOS“精确:关闭”),则每个修复都是操作系统策略的近似值 - 检查 accuracyAuthorization / reducedAccuracy

为什么 getCurrentPosition() 在某些手机上使用 LOCATION_FAILURE 会失败,但在其他手机上却可以?

PlatformException(LOCATION_FAILURE, "Failed to obtain location") 意味着一次性请求无法在 timeout 内获得新的修复,并且没有可以回退到的缓存位置。这不是代码中的错误,而是设备的 GPS/融合堆栈未能及时提供修复。高精度一次性要求“全新”修复,并且是否在(例如)30 秒内成功在很大程度上取决于设备和环境:

  1. “Google 位置准确性”已关闭设置 → 位置 → 位置服务 → Google 位置准确性(Wi-Fi/蓝牙扫描)。启用后,融合提供商几乎立即返回室内 Wi-Fi/蜂窝网络修复;当关闭时,手机必须等待原始 GPS 定位,而该定位永远不会到达室内。 这是“可以在我的手机上运行,​​但不能在他们的手机上运行”的第一大原因。
  2. 室内/地下/无天空视野 — 冷 GPS 定位需要天空可见性,预算芯片组的冷首次定位 (TTFF) 可能需要超过 30 秒,而旗舰产品可在几秒钟内获得辅助 GPS 定位。
  3. 在操作系统级别禁用 GPS 提供商(仅网络位置)——纯粹的高精度请求没有任何可锁定的内容。
  4. Google Play 服务缺失/过时(某些华为/AOSP 版本)- 融合客户端无法运行。
  5. 样本计数 — 使用 samples: 3 Tracelet 必须收集三个修复;在边缘信号中,它可能会先获得一个并在其余信号之前超时。 samples: 1 is more forgiving indoors.

如何使其可靠 - 回退到最后已知的位置:

Future<tl.Location?> bestPosition() async { try { return await tl.Tracelet.getCurrentPosition( desiredAccuracy: tl.DesiredAccuracy.high, timeout: 60, // cold GPS fixes on budget phones can exceed 30 s samples: 1, // more forgiving indoors than 3 maximumAge: 30000, // accept a <30 s-old cached fix instantly ); } on PlatformException catch (e) { if (e.code == 'LOCATION_FAILURE') { // Weak/indoor signal — fall back to the cached fix before giving up. return await tl.Tracelet.getLastKnownLocation(); } rethrow; } }
  • maximumAge 立即返回最近缓存的修复,而无需唤醒 GPS - 非常适合 30 秒旧位置即可的出勤/签到。
  • getLastKnownLocation() 永远不会激活提供程序并返回融合缓存所保存的任何内容,因此只有在确实没有可用的内容时才会显示“弱信号”消息。
  • 在启动后的首次修复中保持 timeout 慷慨(45-60 秒),并在室内修复持续失败时提示用户启用 Google 位置精度(通过 getProviderState() / getHealth() 检测提供商状态)。

背景与终止

应用程序关闭或滑开后,Tracelet 是否会继续跟踪?

Android — 是的,使用 stopOnTerminate: false 当用户将应用程序从最近使用的应用程序中删除时,Tracelet 将跟踪移交给不需要 Flutter 引擎的本机后台服务,因此位置捕获和同步将继续。持久的前台服务通知使该服务保持活动状态。使用 stopOnTerminate: true,跟踪会按预期在滑动时停止。

iOS - 这取决于应用程序的关闭方式。 如果 iOS 由于内存/系统原因终止应用程序,它会在下一次重大位置更改时在后台重新启动它并恢复。如果 用户 强制退出应用程序(在应用程序切换器中向上滑动),iOS 会故意暂停其所有位置服务,直到再次打开应用程序 - Apple 不会让任何 SDK 覆盖这一点。


离线和同步

如果设备没有互联网,我的位置会怎样?

不会丢失任何内容。 每个修复都会在捕获后立即写入设备上的数据库(在 encryptDatabase: true 时加密),与网络无关。连接恢复时自动同步上传它们,并以指数退避(maxRetriesretryBackoffBaseretryBackoffCap)重试。 只有在服务器确认接收后才会从数据库中删除一批,因此失败或中断的上传只会重试,而不会被删除。

位置缓冲在您的保留限制内(maxDaysToPersistmaxRecordsToPersist);一旦超过这些,最旧的就会被修剪。您还可以使用 disableAutoSyncOnCellular: true 阻止蜂窝网络上传。

当互联网恢复时,tracelet_sync 会自动推送到我的后端吗?

是的,自动且完全在后台运行。 如果您使用 tracelet_sync(或其包装器,如 tracelet_supabase / tracelet_firebase),则无需自己编写任何网络重试逻辑。

当操作系统检测到网络连接已恢复时,本机同步引擎会立即在后台唤醒,并开始按时间顺序批量将缓存位置上传到后端。它会持续上传,直到本地数据库完全跟上服务器的速度,即使在长时间离线后也能确保零数据丢失。


重启和设备解锁

重新启动后,Tracelet 是否会在我解锁设备之前开始跟踪?

否 — 在 Tracelet 恢复之前,重新启动后设备必须至少解锁一次。 这是 Android 平台规则(直接启动/基于文件的加密),而不是 Tracelet 限制,并且它适用于每个位置 SDK。

冷启动后,设备处于直接启动模式,并且大多数应用程序数据仍处于加密状态。 Android 仅在用户第一次解锁设备后**传递 Tracelet 的启动接收器侦听的 BOOT_COMPLETED 广播(PIN/图案/密码/生物识别)。在此之前,保存 Tracelet 的配置、状态和位置数据库的凭证加密存储将无法访问 — 无法读取或写入任何内容。

因此,重新启动后的顺序是:

  1. 设备启动 → Tracelet 空闲。
  2. 用户解锁一次 → BOOT_COMPLETED 触发 → Tracelet 恢复跟踪和同步。

只有第一个解锁才重要。之后,屏幕可以再次锁定(手机放在口袋中,屏幕关闭)并正常继续跟踪/同步。

启动恢复的要求: startOnBoot: truestopOnTerminate: false 和后台位置(“始终”)权限已授予。在 Android 14+ 上,操作系统还禁止从启动时启动 location 前台服务,因此 Tracelet 会回退到 WorkManager/警报跟踪(无持久通知),直到下次打开应用程序为止。

iOS 根本无法在重新启动时自动启动 - 应用程序无法在启动时运行并保持未启动状态,直到用户打开应用程序或重大位置更改重新启动它,而这本身仅在第一次重新启动后解锁后发生。

它可以在第一次解锁之前追踪吗?

默认情况下不是。在解锁之前捕获位置需要 Android 直接启动,这意味着将 Tracelet 所需的数据移动到设备加密存储中 - 在用户进行身份验证之前可读,这是比 Tracelet 通常使用的凭据加密(以及可选的 encryptDatabase 保护)存储更弱的静态保证。为了充分保护您的数据,Tracelet 启用开箱即用的直接启动。如果预解锁跟踪是您的用例的硬性要求,则可以将其作为高级应用程序级选择启用 - 在依赖它之前先进行接触。


地理围栏

当我使用模拟/模拟位置进行测试时,地理围栏转换不会触发(它在 1.x 中工作)

**模拟位置仍然有效 - 但在高精度地理围栏模式下,在评估地理围栏之前,您的模拟修复将被过滤掉。**使用 geofenceModeHighAccuracy: true,从连续 GPS 流的 应用程序内 计算转换,并且评估器仅在 通过 位置过滤器的修复上运行。路线模拟工具通常在点之间“传送”,而这些跳跃会被以下情况拒绝:

  • maxImpliedSpeed — 两个相距较远的样本之间令人难以置信的速度被视为异常值并被丢弃。
  • useKalmanFilter: true — 更流畅的战斗可以应对突然的、非物理的模拟跳跃。
  • trackingAccuracyThreshold — 一些模拟提供商报告 accuracy = 0 或未达到阈值的不切实际的值。

放置在您的当前位置上的地理围栏仍然会触发,因为 geofenceInitialTriggerEntry: true 在注册时会发出 ENTER(无需移动) - 因此它永远不会通过过滤器。

对于高精度模式下的模拟测试,放宽过滤器:

filter: tl.LocationFilter( rejectMockLocations: false, useKalmanFilter: false, // disable smoothing maxImpliedSpeed: 0, // 0 disables the implied-speed reject trackingAccuracyThreshold: 0, // accept regardless of reported accuracy ), geo: tl.GeoConfig(distanceFilter: 0, disableElasticity: true),

还要在开发人员选项→选择模拟位置应用程序下选择您的模拟应用程序,并以小而实际的步骤推进模拟路线。

最简单的路径是在标准地理围栏模式(geofenceModeHighAccuracy: false)下进行测试,该模式委托给操作系统地理围栏服务。操作系统根据融合的提供商进行评估,并尊重系统模拟位置应用程序无需 SDK 过滤器 - 在那里使用 ≥ ~100 m 的半径,因为操作系统强制执行实际的最小值,并且低于该值的小/退出转换是不可靠的。

使用 debug: truelogLevel: verbose,查看 Location filtered by Rust processor: <reason> 的日志 — 它会准确告诉您哪个过滤器丢弃了每个模拟修复。

数据安全与隐私

我的位置数据静态时是否加密?

可选,是的。设置 encryptDatabase: true 以加密缓冲位置的本地 SQLite 数据库。在 Android 上,这使用 SQLCipher (AES-256),并要求您将 SQLCipher 依赖项添加到您的应用程序中 - 它是可选的,因此默认构建保持较小,并且在没有它的情况下调用 encryptDatabase 会引发明显的错误。在 iOS 上,加密存储是本地处理的。

为了防篡改而不是保密,审计跟踪 (audit.enabled) 哈希链每个记录(例如 SHA-256),以便您可以证明历史记录在事后没有被更改。 隐私区域允许您抑制或编辑敏感区域(例如用户的家)内的修复。

Tracelet 是否检测模拟/伪造的 GPS 位置?

是的,通过 mockDetectionLevel 上的 LocationFilter

  • disabled(默认)— 无条件接受所有位置。
  • basic — 信任平台的“是模拟”标志。
  • heuristic — 平台标志 加上 本机启发式和 Dart 端时间戳检查,以捕获隐藏该标志的欺骗者。

heuristic,每个 Location 都注释了为什么它被判断为真或假,因此您可以按照自己的逻辑接受、标记或拒绝模拟修复。


从 flutter_background_geolocation 迁移

我来自 flutter_background_geolocation — 切换有多难?

Tracelet 的 API 有意接近 flutter_background_geolocation,因此大多数应用程序都以最小的更改进行映射 — ready/start/stop、位置/运动/提供商事件和 HTTP 同步都有直接等效项。请参阅完整的迁移指南 ,了解配置/事件映射表以及需要注意的一些行为差异。