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_*)

Включите интеграцию с диспетчером пакетов Swift Flutter. Ядро Rust Tracelet (TraceletCore.xcframework, которое предоставляет символы UniFFI, используемые tracelet_ios), связывается через диспетчер пакетов Swift. На устаревшем пути только для CocoaPods платформа не связана с целью 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; вам не нужно менять его для каждого проекта.
  • flutter clean + новый pod install сам по себе не решит эту проблему — недостающим элементом является SPM, связывающий инфраструктуру Rust, а не устаревшие поды.

Батарея и датчики движения

Датчик движения разряжает больше батареи при ходьбе?

Нет, это действительно экономит батарею. Аппаратные датчики движения (акселерометр/детектор шагов) потребляют меньше батареи 0.1% в час. Tracelet использует этот датчик со сверхнизким энергопотреблением для полного отключения чрезвычайно энергоемкого чипа GPS (который потребляет 4-8% в час), когда телефон лежит на столе.

Когда вы начинаете идти, датчик движения немедленно активирует GPS для записи поездки. Общий результат — огромный положительный результат в отношении срока службы батареи по сравнению с традиционным отслеживанием.

Почему я не получаю постоянные обновления, когда телефон не двигается?

Это намеренно — это самая большая экономия заряда батареи, которую обеспечивает Tracelet. Когда обнаружение движения определяет, что устройство неподвижно, Tracelet отключает непрерывный GPS и переключается в режим низкого энергопотребления (периодические однократные исправления или мониторинг геозон). В тот момент, когда реальное движение возобновится, непрерывное отслеживание автоматически активируется.

Если вам по-прежнему нужно периодическое определение местоположения «Я здесь», пока вы неподвижны, установите heartbeatInterval (секунды). Небольшой дрейф GPS при парковке не увеличивает показания одометра — замеры хуже, чем odometerAccuracyThreshold (по умолчанию 50 м), исключаются из расстояния.

Как еще больше сократить расход заряда батареи?

Tracelet уже отключает GPS, когда он неподвижен, но у вас есть несколько рычагов:

  • Ресурс батареи — установите batteryBudgetPerHour в GeoConfig (например, 3.0 для 3 %/час). Затем Tracelet автоматически корректирует distanceFilter и desiredAccuracy во время выполнения, чтобы оставаться ниже этой цели.
  • Выпуск Wakelock — установите releaseWakelockWhenStationary: true в AndroidConfig, чтобы ЦП мог полностью спать, когда пользователь неподвижен (требуется MotionDetectionMode.smart).
  • Фильтр расстояний — большее значение distanceFilter (метры) позволяет регистрировать меньше исправлений во время движения.
  • Меньшая точностьdesiredAccuracy из medium/low потребляет меньше энергии, чем high/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 содержит enabled, status (авторизация), gps, network, accuracyAuthorization, gpsFallback и mockLocationsDetected. То же событие доставляется обратному вызову headless в фоновом/уничтоженном состоянии (если оно зарегистрировано) и сохраняется как запись providerchange, если вы не установите disableProviderChangeRecord: true.

Поведение по штатам и платформам

ГосударствоАндроидiOS
Передний планproviderchange (enabled: false) срабатывает; служба переднего плана остается активной; никаких новых исправлений до повторного включения.providerchange сгорает; didFailWithError обрабатывается корректно (однократные снимки возвращаются к последнему известному местоположению); никаких новых исправлений.
ФонСлужба переднего плана (и ее уведомление) продолжает работать; объединенные обновления просто останавливаются; providerchange все еще отправлено. Возобновляется при повторном включении.Подписка CLLocationManager остается зарегистрированной; iOS ничего не доставляет до тех пор, пока местоположение не вернется, а затем возобновляет работу (и может перезапустить приложение через регион/SLC).
Устранен (убит)Если активен фоновый/загрузочный сеанс (stopOnTerminate: false/startOnBoot), служба ведет себя как Background, как описано выше. Если процесс неактивен, ничего не запускается до тех пор, пока ОС не запустит его в следующий раз.iOS перезапускает приложение с помощью мониторинга значимого местоположения/региона только при возникновении события местоположения/региона — чего невозможно, если местоположение отключено. После повторного включения следующее квалификационное мероприятие перезапускается и возобновляется.

Во всех случаях состояние сеанса сохраняется, поэтому повторное включение местоположения автоматически возобновляет отслеживание без повторной инициализации.

Что Tracelet не делает

  • Он не автоматически останавливает сеанс или очищает вашу конфигурацию/состояние.
  • Он не выдает ошибку или дает сбой — ловится ошибка платформы «локация отключена/отказано».
  • Он не создает местоположения (за исключением точного расчета Android, см. ниже) — в вашей базе данных просто есть пробел для периода, когда местоположение было отключено.

Особенности платформы, которые стоит знать

Android — если пользователь отключает GPS, но Wi-Fi/позиционирование ячейки все еще включено, Tracelet автоматически переходит к позиционированию со сбалансированной мощностью и выдает providerchange с gpsFallback: true, восстанавливая полную точность при возвращении сигнала GPS. Эти приблизительные исправления записываются и синхронизируются (с тегами locationSource и их настоящим accuracy). Если enableDeadReckoning: true, после потери GPS в течение настроенной задержки Tracelet оценивает положение по датчикам движения до тех пор, пока не будет получено реальное местоположение. Постоянное уведомление остается видимым повсюду.

iOS — Ошибка requestLocation() разрешает одноразовые запросы с использованием последнего известного местоположения, а не зависает. iOS не отправляет фоновых событий или событий перезапуска, пока местоположение отключено; доставка и перезапуск в выключенном состоянии возобновляются после повторного включения.

Что вам следует делать в своем приложении

  1. Подпишитесь на onProviderChange и размещайте баннер/диалог при enabled == false.
  2. При необходимости направьте пользователя к настройкам через Tracelet.openLocationSettings().
  3. Не повторно вызывать start() при повторном включении — Tracelet возобновляет работу самостоятельно; вызов start() безвреден, но не нужен.

Отзыв разрешения приложения* вместо выключения переключателя

Отключение переключателя влияет на все приложения и полностью восстанавливается, как указано выше. Об отзыве разрешения на определение местоположения приложения (или понижении версии «Всегда» → «Во время использования») сообщается через то же событие providerchange через поля status / accuracyAuthorization. В Android 12+, если разрешение на фоновое местоположение потеряно, загрузка/фоновый перезапуск намеренно пропускается (в противном случае произойдет сбой) — повторно предоставьте разрешение и повторите инициализацию для возобновления.

Насколько точны местоположения и как отличить GPS от исправлений Wi-Fi/сотовой связи?

Каждый Location содержит реальный coords.accuracy в метрах и тег locationSource: "gps" (≤50 м), "wifi" (≤200 м), "cell" (хуже) или "network" (во время отказа GPS). Tracelet не автоматически удаляет исправления с низкой точностью — он записывает их, чтобы ваш след оставался непрерывным, — но он не допускает плохих исправлений к одометру (odometerAccuracyThreshold, по умолчанию 50 m) и отклоняет невозможные скачки скорости (maxImpliedSpeed).

Если вам нужны только данные GPS-качества, отфильтруйте на своей стороне по locationSource == "gps" или accuracy <= 50. Если пользователь предоставил только приблизительное/грубое местоположение (или iOS «Точное: выключено»), каждое исправление является приблизительным в соответствии с политикой ОС — установите флажок accuracyAuthorization / reducedAccuracy.

Мой трек прыгает влево и вправо при ходьбе, а расстояние в 2–3 раза выше, чем у Strava. Как это исправить?

Шум GPS записывается как реальное движение. При темпе ходьбы (~ 1,4 м/с) горизонтальная ошибка телефона составляет 5–8 м под открытым небом и значительно хуже под покровом деревьев или между зданиями — значительная часть того, как далеко пользователь фактически переместился между двумя точками. Каждый «всплеск» дрожания в сторону — это настоящее изменение координат, и одометр, который доверяет каждому исправлению, добавляет все это. Фитнес-приложения выглядят более плавно, потому что они настроены специально для ходьбы; настройки SDK по умолчанию намеренно нейтральны для транспортных средств, велосипедистов и пешеходов.

Сначала обновитесь до 3.8.0-beta или более поздней версии. Два дефекта уровня 3.7.6 или ниже вызывали именно этот симптом и не могли быть устранены:

  • useKalmanFilter: true сглаживал только рендеренный трек. Сглаживание выполнялось после процессора местоположения и подавало только coords, поэтому одометр продолжал интегрировать необработанный джиттер — карта выглядела чистой, а расстояние оставалось неверным. Теперь он работает перед фильтрами, поэтому расстояние, гейты точности/предполагаемой скорости и одометр видят трассу без шума. Ожидайте, что после обновления ваши записанные расстояния уменьшатся; это падение — это шум, который вы ранее считали.
  • Исправление, слишком грубое для odometerAccuracyThreshold, было правильно исключено из одометра, но точка отсчета одометра все равно приближалась к нему - поэтому земля, пройденная на этом сегменте, была потеряна навсегда. Затягивание ворот приводило к уменьшению расстояния, а не к его исправлению. Грубое исправление теперь откладывает свое расстояние, а следующее надежное исправление записывает весь диапазон.

Тогда перестаньте угадывать пороговые значения — позвольте классификатору на устройстве выбрать их:

await tl.Tracelet.ready(tl.Config( geo: tl.GeoConfig( filter: tl.LocationFilter(useKalmanFilter: true), ), classifier: tl.ClassifierConfig( enableFusedClassifier: true, // required — the classifier must be running autoTuneFromTransportMode: true, ), ));

Классификатор объединяет частоту шагов акселерометра со скоростью GPS, чтобы разделить still / walking / running / cycling / vehicle, и когда режим фиксируется, он меняет местами distanceFilter, trackingAccuracyThreshold, odometerAccuracyThreshold и maxImpliedSpeed на подходящие для него значения. См. автонастройку по режиму транспорта для гарантий (только зафиксированные изменения, пороговые значения меняются местами, чтобы показания одометра оставались непрерывными, unknown восстанавливает ваши собственные значения).

С debug: true и logLevel: verbose вы увидите каждую перенастройку в журнале:

auto-tune: 'walking' → distanceFilter=8.0m trackingAccuracy=15m odometerAccuracy=10m maxImpliedSpeed=4m/s

Существуют ли рекомендуемые настройки для ходьбы, бега и бега?

Это точные значения, к которым применяется автонастройка, поэтому вы можете установить их вручную, если ваше приложение отслеживает только одно действие (отслеживание пробежек, приложение для доставки пешком), и вы предпочитаете не запускать классификатор:

ДеятельностьНОТРАНС0ЛАТЕНОТРАНС1ЛАТЕНОТРАНС2ЛАТЕНОТРАНС3ЛАТЕ
Стационарные25 м15 м10 м3 м/с
Прогулка8 м15 м10 м4 м/с
Бег/бег12 м25 м15 м9 м/с
Велоспорт20 м30 м20 м20 м/с
Вождение30 м50 м30 м60 м/с

Бег не является отдельной предустановкой. Он находится внутри бегового диапазона (6–20 км/ч) и имеет общие пороговые значения — «предустановка для бега», отличная от бега, будет изобретена с точностью, а не с измерением.

Приложение только для ходьбы со сглаживанием:

geo: tl.GeoConfig( distanceFilter: 8, filter: tl.LocationFilter( useKalmanFilter: true, trackingAccuracyThreshold: 15, odometerAccuracyThreshold: 10, maxImpliedSpeed: 4, ), ),

Если вы уже настроили вручную и длина еще ~50%, проверьте эти три вещи:

  • distanceFilter ниже минимального уровня шума GPS учитывает шум как расстояние. При distanceFilter: 5 стационарная ошибка в 6 м считывается как 6 м ходьбы. Держите его на уровне примерно 8 м или выше — как ни странно, запись меньшего количества точек дает более точную сумму.
  • Жесткий trackingAccuracyThreshold делает показания одометра неактуальными. При использовании trackingAccuracyThreshold: 10 каждое выживающее исправление уже составляет ≤ 10 м, поэтому odometerAccuracyThreshold (50 по умолчанию м) никогда ничего не отклоняет. Ворота, на которые вы рассчитывали, ничего не делают; ошибка возникает из-за исправлений, которые вы сохранили, а не из тех, которые вы удалили.
  • useKalmanFilter по умолчанию отключен, а до 3.8.0 он вообще не влиял на расстояние, даже когда был включен.

Не ждите точного совпадения с другим приложением. Strava, Google Fit и Tracelet используют разные потоки исправлений (периодичность выборки, настройки поставщика, состояние приложения на переднем плане) и применяют различное сглаживание. Согласование в пределах нескольких процентов по маршруту известной длины является реалистичной целью — измеряйте его по маршруту, который вы можете проверить, а не по номеру другого приложения.

Почему getCurrentPosition() не работает с LOCATION_FAILURE на некоторых телефонах, но работает на других?

PlatformException(LOCATION_FAILURE, "Failed to obtain location") означает, что однократный запрос не смог получить свежее исправление в timeout и не имел места в кэше, к которому можно было бы вернуться. Это не ошибка в вашем коде — это GPS/слитый стек устройства, который не может вовремя исправить ошибку. Высокоточный одноразовый сеанс требует нового исправления, и успешность этого в течение (например) 30 с во многом зависит от устройства и среды:

  1. Точность определения местоположения Google отключенаНастройки → Местоположение → Службы определения местоположения → Точность определения местоположения Google (сканирование Wi-Fi/Bluetooth). При включении объединенный провайдер почти мгновенно возвращает соединение Wi-Fi/сотовой связи в помещении; в выключенном состоянии телефон должен ждать необработанного сигнала GPS, который никогда не поступает в помещение. Это причина №1 «работает на моем телефоне, а не на их».
  2. В помещении/под землей/без вида на небо — для холодного определения координат GPS требуется видимость неба, а бюджетные чипсеты могут использовать более 30 секунд для первого холодного определения координат (TTFF), в то время как флагманы получают исправления с помощью GPS за секунды.
  3. Поставщик GPS отключен на уровне ОС (местоположение только по сети) — чистый высокоточный запрос не на чем фиксироваться.
  4. Сервисы Google Play отсутствуют/устарели (некоторые сборки Huawei/AOSP) — объединенный клиент не запускается.
  5. Счетчик выборок — при использовании samples: 3 Tracelet должен собрать три исправления; при пограничном сигнале он может получить один и истечь раньше остальных. samples: 1 более щаден в помещении.

Как сделать это надежным — вернитесь к последнему известному местоположению:

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 переопределить это.

Я установил showNotificationOnPauseOnly: true, но уведомление всегда видно. Почему?

Потому что stopOnTerminate — это false, и обе настройки не могут учитываться одновременно.

Скрытие уведомления означает понижение статуса службы переднего плана (в Android нет службы переднего плана без уведомления), а процесс, в котором нет службы переднего плана, Android убивает, когда пользователь вытаскивает приложение из списка последних. Таким образом, хотя уведомление было скрыто, stopOnTerminate: false ничего не гарантировал: пролистывание внутри промежутка (измерение составило 285 мс на Pixel Fold, 700–1500 мс на устройстве Android 15) сразу уничтожило процесс, без каких-либо обезглавленных задач, без событий и журналов. Никакие действия SDK во время удаления задачи не могут этому помешать — Android уже выбрал, какие процессы нужно уничтожить, прежде чем сообщит вашему приложению, что задача была удалена.

Tracelet решает эту проблему в пользу обещания выживания: при использовании stopOnTerminate: false флаг игнорируется, а уведомление остается. Он регистрирует причину один раз для каждого start() в канале жизненного цикла, поэтому Tracelet.getLogs() покажет ее.

Установите stopOnTerminate: true, если вы предпочитаете скрытое уведомление и хотите, чтобы отслеживание прекращалось при удалении приложения. Полное объяснение 📖


Офлайн и синхронизация

Что произойдет с моим местоположением, если на устройстве нет Интернета?

Ничего не потеряно. Каждое исправление записывается в базу данных на устройстве (зашифрованную при использовании encryptDatabase: true) в момент его записи — независимо от сети. Автоматическая синхронизация загружает их при восстановлении подключения, повторяя попытку с экспоненциальной задержкой (maxRetries, retryBackoffBase, retryBackoffCap). Пакет удаляется из базы данных только после того, как сервер подтвердит получение, поэтому неудачная или прерванная загрузка просто повторяется, а не удаляется.

Буфер местоположений находится в пределах ваших ограничений хранения (maxDaysToPersist, по умолчанию — 3 дня, и maxRecordsToPersist, по умолчанию неограниченно); самые старые обрезаются, как только они превышаются. Установите любое значение -1, чтобы отключить это ограничение — стоит сделать это намеренно, если вы ожидаете, что автономный режим продлится дольше, чем окно по умолчанию, поскольку запись, удаленная по принципу хранения, никогда не загружалась. До версии 3.8.3 ни одно ограничение фактически не применялось, поэтому очередь росла без ограничений. Вы также можете приостановить загрузку через сотовую сеть с помощью disableAutoSyncOnCellular: true.

Будет ли Tracelet_sync автоматически пересылаться на мой сервер, когда снова появится Интернет?

Да, автоматически и полностью в фоновом режиме. Если вы используете tracelet_sync (или его оболочки, такие как tracelet_supabase / tracelet_firebase), вам не нужно самостоятельно писать логику повторных попыток сети.

Когда ОС обнаруживает, что сетевое подключение восстановлено, встроенный механизм синхронизации немедленно просыпается в фоновом режиме и начинает загрузку кэшированных местоположений на сервер в хронологических пакетах. Он продолжает загрузку до тех пор, пока локальная база данных не будет полностью подключена к серверу, обеспечивая нулевую потерю данных даже после длительных периодов автономной работы.


Перезагрузка и разблокировка устройства

После перезагрузки Tracelet начинает отслеживать до того, как я разблокирую устройство?

Нет — устройство должно быть разблокировано хотя бы один раз после перезагрузки, прежде чем Tracelet возобновит работу. Это правило платформы Android (прямая загрузка/шифрование на основе файлов), а не ограничение Tracelet, и оно применяется к каждому местоположению SDK.

После холодной загрузки устройство находится в режиме Прямая загрузка, и большая часть данных приложения по-прежнему зашифрована. Android передает только широковещательную рассылку BOOT_COMPLETED, которую прослушивает загрузочный приемник Tracelet после того, как пользователь разблокирует устройство в первый раз (PIN-код/шаблон/пароль/биометрические данные). До этого момента хранилище, зашифрованное учетными данными, в котором хранится база данных конфигурации, состояния и местоположения Tracelet, недоступно — нечего читать или записывать.

Таким образом, последовательность действий после перезагрузки следующая:

  1. Устройство загружается → Tracelet неактивен.
  2. Пользователь разблокируется → срабатывает BOOT_COMPLETED → Tracelet возобновляет отслеживание и синхронизацию.

Имеет значение только первая разблокировка. После этого экран можно снова заблокировать (телефон в кармане, экран выключен) и отслеживание/синхронизация продолжится в обычном режиме.

Требования для возобновления загрузки: предоставлено разрешение startOnBoot: true, stopOnTerminate: false и фоновое местоположение («Всегда»). В Android 14+ ОС дополнительно запрещает запуск службы локации переднего плана при загрузке, поэтому Tracelet возвращается к WorkManager/отслеживанию тревог (без постоянных уведомлений) до следующего открытия приложения.

iOS вообще не может автоматически запускаться при перезагрузке — приложения не могут запускаться при загрузке и оставаться незапущенными до тех пор, пока пользователь не откроет приложение или если существенное изменение местоположения не перезапустит его, что само по себе происходит только после первой разблокировки после перезагрузки.

Может ли он отслеживать до первой разблокировки?

Не по умолчанию. Для захвата местоположений перед разблокировкой требуется Android Direct Boot, что означает перемещение данных, необходимых Tracelet, в хранилище зашифрованное устройством, доступное для чтения до аутентификации пользователя, что является более слабой гарантией при хранении, чем хранилище с шифрованием учетных данных (и, возможно, защищенное encryptDatabase), которое Tracelet использует обычно. Чтобы обеспечить полную защиту ваших данных, Tracelet не включает прямую загрузку «из коробки». Если отслеживание предварительной разблокировки является жестким требованием для вашего варианта использования, его можно включить как расширенную подписку на уровне приложения — обратитесь к нам, прежде чем полагаться на него.


Геофенсинг

Переходы геозон не срабатывают, когда я тестирую макеты/моделированные местоположения (это работало в версии 1.x)

Имитируемые местоположения по-прежнему работают, но в режиме высокоточной геозоны ваши ложные исправления отфильтровываются перед оценкой геозоны. При использовании geofenceModeHighAccuracy: true переходы вычисляются в приложении из непрерывного потока GPS, и оценщик работает только с исправлениями, которые проходят фильтр местоположения. Инструменты моделирования маршрута обычно «телепортируются» между точками, и эти прыжки отклоняются:

  • maxImpliedSpeed — неправдоподобная скорость между двумя далеко расположенными выборками рассматривается как выброс и отбрасывается.
  • useKalmanFilter: true — более плавные бои с резкими, нефизическими имитационными прыжками.
  • trackingAccuracyThreshold — некоторые провайдеры макетов сообщают о accuracy = 0 или нереалистичном значении, не соответствующем пороговому значению.

Геозона, размещенная в вашем текущем местоположении, по-прежнему срабатывает, поскольку geofenceInitialTriggerEntry: true выдает ENTER при регистрации (движение не требуется) — поэтому она никогда не проходит через фильтр.

Для пробного тестирования в режиме высокой точности ослабьте фильтры:

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

Также выберите макет приложения в разделе Параметры разработчика → Выбрать приложение макета местоположения и продвигайтесь по моделируемому маршруту небольшими и реалистичными шагами.

Самый простой путь – тестирование в стандартном режиме геозоны (geofenceModeHighAccuracy: false), который делегирует службу геозоны ОС. ОС выполняет оценку с помощью объединенного поставщика и учитывает системное приложение для имитации местоположения без фильтров SDK — используйте там радиус ≥ ~100 м, поскольку ОС обеспечивает практический минимум, а переходы small/EXIT ниже этого значения ненадежны.

При использовании debug: true и logLevel: verbose следите за журналами Location filtered by Rust processor: <reason> — они точно сообщают вам, какой фильтр удалил каждое имитируемое исправление.

Почему я вижу повторяющиеся ВХОД/ВЫХОД для одной и той же геозоны, особенно на агрессивных OEM-телефонах Android (Xiaomi, Vivo, Oppo…)?

Tracelet уже сохраняет безопасное для возобновления состояние «известное внутри», которое выдерживает смерть процесса, поэтому OEM-производитель, завершающий и перезапускающий ваше приложение, не сам по себе повторно запускает ENTER для устройства, которое никогда не покидало зону.

Обычной причиной является то, что хост-приложение вызывает removeGeofences() непосредственно перед addGeofence() при каждом запуске, чтобы «обновить» зону:

// Don't do this on every app start: await tl.Tracelet.removeGeofences(); await tl.Tracelet.addGeofence(tl.Geofence(identifier: 'OFFICE', latitude: lat, longitude: lng, radius: radius));

removeGeofences() намеренно очищает сохранившееся внутреннее состояние — это необходимо, поскольку ограждение действительно исчезло. Поскольку addGeofence() уже обновляет идентификатор, его повторный вызов с новыми координатами обновляет существующую границу:

// Do this instead — updates OFFICE if it exists, creates it if not: await tl.Tracelet.addGeofence(tl.Geofence(identifier: 'OFFICE', latitude: lat, longitude: lng, radius: radius));

Вызывайте removeGeofence()/removeGeofences() только тогда, когда ограждение фактически удаляется, а не для обновления существующего ограждения с тем же идентификатором. На устройстве, которое редко выходит из строя, путь инициализации «удалить-затем-добавить» запускается один раз при холодном запуске и является невидимым. На агрессивных OEM-производителях он может перезапускаться десятки раз в час, поскольку ОС постоянно убивает и перезапускает ваше приложение, и при каждом запуске стирается внутреннее состояние прямо перед тем, как приходит настоящее исправление GPS, каждый раз создавая свежий, технически правильный ENTER с теперь пустой точки зрения SDK.

Безопасность и конфиденциальность данных

Зашифрованы ли мои данные о местоположении при хранении?

Опционально, да. Установите 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 — насколько сложно переключиться?

API Tracelet намеренно близок к flutter_background_geolocation, поэтому большинство приложений преобразуются с минимальными изменениями — ready/start/stop, события местоположения/движения/поставщика и синхронизация HTTP имеют прямые эквиваленты. См. полное руководство по миграции  для получения информации о таблице сопоставления конфигурации/событий и нескольких поведенческих различиях, на которые следует обратить внимание.