Android SDK: укрощение диспетчера батареи
Android печально известен агрессивным уничтожением фоновых служб. Китайские OEM-производители (такие как Xiaomi, Huawei) даже имеют специальные менеджеры питания, которые полностью нарушают стандартные ограничения фонового выполнения Android.
На этой странице объясняется, почему Android убивает ваше приложение и как конкретные конфигурации Android Tracelet решают эту проблему в реальном мире.
Дополнительная зависимость: высокоточное определение местоположения GMS.
Android SDK Tracelet может использовать Местоположение сервисов Google Play — FusedLocationProviderClient
(лучшая точность и расход заряда батареи), аппаратное распознавание активности (ходьба/вождение/неподвижность),
и аппаратное обеспечение геофенсинга. Чтобы сделать SDK легким и удобным для использования на устройствах без Google, это
зависимость не входит в комплект. Вы добавляете его только в том случае, если вам нужен путь высокой точности:
// android/app/build.gradle.kts
dependencies {
implementation("com.google.android.gms:play-services-location:21.3.0")
}Это необязательно, и SDK постепенно ухудшается. Без play-services-location Tracelet
возвращается к стандарту AOSP LocationManager (обычный GPS). Отслеживание по-прежнему работает — просто
потеряете преимущества точности/аккумулятора, распознавание активности оборудования и аппаратные преимущества плавленого поставщика.
геозоны. Рекомендуется добавить его, если вы специально не ориентируетесь на устройства без Google или AOSP.
(например, Huawei без GMS или ПЗУ без Google).
Минимальная версия: 21.2.0. Код Tracelet для Android вызывает
API FusedLocationProviderClient и ActivityRecognitionClient, которые только стали
интерфейсы в play-services-location 21.2.0. Более старые версии (например, 19.0.0) поставляются с ними.
как конкретные классы, поэтому вызов их выбрасывает
java.lang.IncompatibleClassChangeError во время выполнения. Tracelet публикует Gradle
зависимость ограничение, которое автоматически переводит play-services-location в 21.2.0+, если
другая зависимость использует более старую версию, но если вы закрепите версию самостоятельно, сохраните ее по адресу
21.2.0 или новее (мы рекомендуем 21.3.0).
С play-services-location | Без него (резервный вариант AOSP) |
|---|---|
| Расположение предохранителя (лучшая точность + батарея) | Необработанный GPS/сеть через LocationManager |
Распознавание активности оборудования (onActivityChange) | Обнаружение движения только с помощью акселерометра |
| Аппаратное геозонирование | Программное обеспечение (в SDK) для оценки геозон |
Необязательная зависимость: Play Integrity (аттестация устройства).
Если вы используете функцию аттестации устройства Tracelet (AttestationConfig), Android
сторона генерирует свой криптографический токен через Целостность Google Play. Точно так же, как
play-services-location, эта зависимость не входит в комплект — вы добавляете ее только тогда, когда
включить аттестацию:
// android/app/build.gradle.kts
dependencies {
implementation("com.google.android.play:integrity:1.6.0")
}Это необязательно, и аттестация постепенно ухудшается. Без целостности игры.
зависимости, все остальное в Tracelet работает нормально — затрагивается только аттестация:
AttestationConfig(enabled: true) регистрирует предупреждение, а Tracelet.getAttestationToken()
вместо токена возвращает null вместо сбоя. Добавляйте зависимость, только если вы
включите аттестацию и вам нужен вердикт Play Integrity на Android. См.
Руководство по аттестации устройств
для полного рабочего процесса и проверки на стороне сервера.
Это должно быть implementation, а не compileOnly. Честность игры внутри compileOnly.
Tracelet SDK (поэтому он никогда не поставляется и не применяется в приложениях, не использующих аттестацию). Если
вы включаете аттестацию без добавления зависимости в модуль app, как
implementation, классы отсутствуют во время выполнения, и аттестация автоматически возвращает null.
Разрешения и настройка манифеста
Tracelet автоматически вводит необходимые разрешения в ваш AndroidManifest.xml при компиляции приложения. По умолчанию он запрашивает:
ACCESS_COARSE_LOCATIONиACCESS_FINE_LOCATION(необходимы для отслеживания)ACCESS_BACKGROUND_LOCATION(требуется для отслеживания закрытия приложения)FOREGROUND_SERVICEиFOREGROUND_SERVICE_LOCATION(требуется для непрерывного фонового выполнения)ACTIVITY_RECOGNITION(требуется для интеллектуального механизма обнаружения движения)POST_NOTIFICATIONS(требуется для пользовательского интерфейса службы переднего плана Android 13+)SCHEDULE_EXACT_ALARM(требуется для точного отслеживания в периодическом режиме)
Удаление неиспользуемых разрешений (слияние манифеста)
Если вашему приложению не нужна определенная функция (например, вашему клиенту не требуется обнаружение движения или вы не используете периодический режим), вы можете принудительно удалить разрешение с помощью директивы tools:node="remove" слияния манифеста Android в вашем уровне приложения AndroidManifest.xml (android/app/src/main/AndroidManifest.xml).
Собственный код Tracelet полностью защищен checkSelfPermission(). Отсутствие разрешений приведет к плавному откату, а не к сбою.
| Разрешение | Эффект при удалении (tools:node="remove") |
|---|---|
| НОТРАНС0ЛАТЕ | Безопасно. Возврат к базовому обнаружению движения только с помощью акселерометра. Поток onActivityChange не сработает (нет классификации ходьбы/бега/вождения). |
| НОТРАНС0ЛАТЕ | Безопасно. Android 10+ ограничивает отслеживание, когда приложение полностью переведено в фоновый режим или удалено. |
| НОТРАНС0ЛАТЕ | Безопасно. В периодическом режиме используются экономичные, но неточные таймеры WorkManager. |
| НОТРАНС0ЛАТЕ | Безопасно. Постоянное уведомление службы переднего плана скрыто на Android 13+. Служба все еще работает. |
| НОТРАНС0ЛАТЕ | Безопасно. Вы не сможете запросить освобождение от оптимизации батареи из ОС. |
FOREGROUND_SERVICE / FOREGROUND_SERVICE_LOCATION | Безопасно для приложений, использующих только геозоны или только периодические действия. Их удаление отключает непрерывное отслеживание переднего плана (start()), но стандартное геозонирование и периодический режим продолжают работать. Требуется для непрерывного фонового отслеживания. |
ACCESS_FINE_LOCATION / COARSE | НЕБЕЗОПАСНО. Без разрешения на определение местоположения Tracelet не сможет получить координаты. |
Правила Google Play в отношении приоритетных служб (вступают в силу 28 октября 2026 г.).
больше не является разрешенным вариантом использования для приоритетного местоположения службы. Если ваше приложение использует
службу переднего плана исключительно для геозон, необходимо удалить
FOREGROUND_SERVICE_LOCATION (и FOREGROUND_SERVICE) из объединенных
манифест. Стандартный режим геозоны Tracelet использует собственный API геозон и не
не запускать службу переднего плана, поэтому приложения, работающие только с геозонами, соответствуют требованиям — просто
удалите разрешения ниже и сохраните foregroundService.enabled = false. См.
Руководство по геозонированию
для получения подробной информации.
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools">
<!-- Geofence-only / periodic-only app: no continuous foreground tracking -->
<uses-permission android:name="android.permission.FOREGROUND_SERVICE_LOCATION" tools:node="remove" />
<uses-permission android:name="android.permission.FOREGROUND_SERVICE" tools:node="remove" />
</manifest>Пример: удаление разрешений на движение и фон
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools">
<!-- Client doesn't want Activity/Motion tracking -->
<uses-permission android:name="android.permission.ACTIVITY_RECOGNITION" tools:node="remove" />
<!-- Client only wants foreground tracking -->
<uses-permission android:name="android.permission.ACCESS_BACKGROUND_LOCATION" tools:node="remove" />
<!-- Remove Exact Alarms -->
<uses-permission android:name="android.permission.SCHEDULE_EXACT_ALARM" tools:node="remove" />
</manifest>Автоматические правила ProGuard/R8
Tracelet активно использует фоновые сервисы Kotlin, привязки JNA для ядра Rust и отражающие каналы Pigeon. Если они запутаны или неправильно сжаты, фоновое отслеживание автоматически завершится неудачей, когда ваше приложение будет скомпилировано для выпуска.
Вам не нужно вручную настраивать ProGuard для Tracelet.
Плагин tracelet_android автоматически поставляется со встроенным consumer-rules.pro. Когда вы создаете приложение Flutter в режиме выпуска, устройство сжатия Android R8 автоматически извлекает эти правила и гарантирует, что все необходимые классы (такие как привязки HeadlessTaskService, BootReceiver и Rust uniffi) выживут при сжатии.
Сценарий 1: Драйвер доставки (непрерывное отслеживание)
Изученные понятия: Режим ожидания, службы переднего плана, Wakelocks.
Проблема
Ваш пользователь — водитель по доставке пиццы. Они положили телефон в карман, выключив экран. Через 15 минут Android перейдет в Режим сна. Он отключает доступ к сети, откладывает фоновые задания и строго ограничивает пробуждение процессора для экономии заряда батареи. Если ваше приложение использует простой таймер для получения GPS, Android просто откажется запускать ваш таймер.
Как Tracelet решает эту проблему: служба переднего плана
Служба переднего плана сообщает Android: * «Эй, я сейчас делаю что-то невероятно важное, и пользователь полностью об этом знает. Не убивайте меня». *
android: tl.AndroidConfig(
foregroundService: tl.ForegroundServiceConfig(
enabled: true,
channelName: 'Delivery Tracking',
notificationText: 'Tracking your route to the customer',
notificationOngoing: true, // User cannot swipe it away
showNotificationOnPauseOnly: false, // Auto-hides when app is open
actions: ['Pause', 'Complete'], // Adds interactive buttons to the notification
),
)Отображая постоянное уведомление в строке состояния, Tracelet повышает приоритет вашего приложения почти до уровня пользовательского интерфейса переднего плана. Android позволит Tracelet работать бесконечно, минуя ограничения режима ожидания.
Настройка внешнего вида уведомлений
Чтобы постоянное уведомление соответствовало фирменному оформлению вашего приложения, вы можете настроить значки и цвета:
android: tl.AndroidConfig(
foregroundService: tl.ForegroundServiceConfig(
enabled: true,
notificationTitle: 'Delivery Mode',
notificationText: 'Tracking your route...',
notificationColor: '#0F9D58', // Your brand hex color
notificationSmallIcon: 'ic_tracelet_icon', // The icon name
notificationLargeIcon: 'ic_large_logo',
),
)Важные правила использования значков:
- Расположение файла: Файл изображения должен быть помещен в доступную для рисования папку вашего проекта Android (
android/app/src/main/res/drawable/ic_tracelet_icon.png). - Ограничения на мелкие значки. Android требует, чтобы
notificationSmallIconбыл абсолютно плоским, прозрачным и только белым. Если вы используете цветной логотип, Android просто отобразит его в виде сплошного серого или белого квадрата. - Свойство Color: Свойство
notificationColorбудет тонировать фон вашего маленького значка в соответствии с вашим брендом.
Android 13+ Примечание. Перед запуском службы необходимо запросить разрешение POST_NOTIFICATIONS, в противном случае уведомление будет автоматически подавляться операционной системой. См. страницу Flutter SDK: Разрешения, чтобы узнать, как именно запросить это из вашего кода Dart.
Обновление уведомления во время отслеживания (updateNotification())
Необходимо изменить уведомление после начала отслеживания — например. обновить заголовок с «В пути» на «Прибытие», поменять текст местами или изменить кнопки действий в середине поездки? Вызов setConfig() с новым ForegroundServiceConfig сохраняет значения, но изменение только для уведомлений не повторно публикует уведомление, которое Android уже показывает — новый контент появится только после несвязанного перезапуска службы или перехода на передний план.
Начиная с v3.6.8, Tracelet.updateNotification() обновляет живое уведомление на месте без перезапуска конвейера отслеживания:
// 1. Apply the new notification content.
await Tracelet.setConfig(
const tl.Config(
android: tl.AndroidConfig(
foregroundService: tl.ForegroundServiceConfig(
notificationTitle: 'Arriving',
notificationText: 'Your driver is 2 minutes away',
),
),
),
);
// 2. Repost the active notification with the new content.
await Tracelet.updateNotification();Безопасно по задумке. updateNotification() никогда не перезапускает отслеживание и не выполняет никаких операций, если служба переднего плана в данный момент не запущена (нечего обновлять). В iOS вместо этого обновляется Live Activity (если вы выбрали его через liveActivityConfig); в Интернете это неактивно, поэтому один и тот же вызов безопасен на любой платформе.
Сценарий 2. Приложение «Погода» (периодическое отслеживание)
Изученные концепции: WorkManager и Exact Alarms
Проблема
Ваш пользователь загрузил местное приложение о погоде. Вы хотите просыпаться в фоновом режиме каждые 4 часа, узнавать их местоположение и загружать местный прогноз.
Вам не нужно постоянное уведомление службы переднего плана. Пользователю не хотелось бы видеть постоянное уведомление «Отслеживание погоды» в своем ящике.
Как Tracelet решает эту проблему: периодический режим
Вместо непрерывного обслуживания Tracelet может использовать планировщики заданий Android, чтобы ненадолго проснуться, получить исправление и снова заснуть.
android: tl.AndroidConfig(
periodicUseForegroundService: false,
periodicUseExactAlarms: true, // Uses AlarmManager instead of WorkManager
)Два планировщика
-
WorkManager (
periodicUseExactAlarms: false) Значение по умолчанию. Это очень экономно расходует аккумулятор. Однако Android решает когда его запустить. Если вы попросите его запускаться каждые 15 минут, Android может подождать 45 минут и запустить его, когда пользователь разблокирует свой телефон для проверки Instagram (так называемая «пакетная обработка»). Это весьма неточно. -
Менеджер Алармов (
periodicUseExactAlarms: true) Если вам абсолютно НЕОБХОДИМО разбудить устройство точно в определенное время, используйте это. Для этого вам необходимо объявить разрешениеSCHEDULE_EXACT_ALARMв вашемandroid/app/src/main/AndroidManifest.xml:<uses-permission android:name="android.permission.SCHEDULE_EXACT_ALARM" />
Примечание. Google Play строго проверяет это разрешение. Используйте его только в том случае, если ваше приложение представляет собой будильник, календарь или абсолютно точно требует точного времени.
Сценарий 3: Агрессия OEM
Изученные концепции: API-интерфейс работоспособности настроек.
Проблема
Вы все сделали правильно. У вас есть служба переднего плана. Но у пользователя есть телефон Xiaomi. В MIUI есть фирменная функция «Экономия заряда», которая принудительно отключает даже службы переднего плана после 5 минут отключения экрана.
Как Tracelet решает эту проблему: автоматическое смягчение последствий и подсказки
Tracelet автоматически применяет внутренние средства защиты, где это возможно, но иногда пользователю физически приходится внести ваше приложение в белый список в собственном меню настроек OEM.
final health = await tl.Tracelet.getSettingsHealth();
if (health['isAggressiveOem'] == true) {
// Automatically opens the manufacturer-specific settings screen
// (e.g. Xiaomi Autostart, Huawei App Launch, Samsung Sleeping Apps)
await tl.Tracelet.showPowerManager();
}Подробный пример того, как реализовать это в пользовательском интерфейсе Flutter, см. на странице Инструменты диагностики и управление питанием.