Перейти к содержимому
Все записи

Типы foreground-сервисов в Android в 2026 году: как выбрать тот, что реально подходит вашей функции

Практическое руководство по ограничениям типов foreground-сервисов Android — dataSync, mediaPlayback, specialUse, shortService — и как выбрать правильный, не получив ни принудительной остановки, ни отказа в проверке.

MFKAPPS 4 мин чтения

Раньше запустить foreground-сервис можно было одной строкой кода и уведомлением. Теперь нет. На актуальном target SDK каждый foreground-сервис обязан объявить тип в манифесте, запросить соответствующее разрешение, а в паре случаев — ещё до публикации приложения оправдать своё существование перед Google. Ничего из этого не бюрократия ради бюрократии — у каждого типа своя гарантия времени жизни, и выбор неправильного типа — это именно то, из-за чего функция, отлично работавшая на тестах, спустя недели тихо убивается на реальном устройстве. Вот как выбирать на практике, на примере решения, которое пришлось принять таймеру фокусировки Mintly.

Почему «просто запустить foreground-сервис» перестало работать

Старая модель была снисходительной: вызываете startForeground(), показываете уведомление, и пока уведомление видно, система в целом вас не трогает. Это превратило foreground-сервисы в магнит для злоупотреблений — сервис «синхронизации», который никогда ничего не синхронизирует, сервис «прослушивания», который просто держит процесс живым. Ответ платформы — привязать каждый foreground-сервис к объявленному типу, у каждого из которых своё разрешение и свой контракт на то, сколько он может работать без присмотра. Больше нельзя сказать «поверьте, это важно». Нужно сказать, какого рода это важно, а остальное система обеспечит сама.

<service
    android:name=".timer.FocusSessionService"
    android:foregroundServiceType="specialUse"
    android:exported="false">
    <property
        android:name="android.app.PROPERTY_SPECIAL_USE_FGS_SUBTYPE"
        android:value="active-focus-timer" />
</service>

Пропустите объявление типа — и сервис просто не запустится на актуальном target SDK: startForeground() не предупреждает, а бросает исключение.

Типы, которые действительно подходят большинству независимых функций

Отобранный список покрывает такие случаи, как camera, microphone, location, phoneCall, mediaPlayback и connectedDevice — каждый строго ограничен тем, что следует из названия, каждый защищён разрешением, соответствующим реальной возможности (FOREGROUND_SERVICE_CAMERA требует CAMERA, и так далее). Если ваша функция действительно воспроизводит звук или транслирует данные с подключённого устройства, тип очевиден, а разрешение, скорее всего, у вас уже есть.

Два типа, которые стоит понять глубже — те, что не привязаны к одному конкретному датчику: dataSync и specialUse.

dataSync предназначен для фоновой передачи данных с чёткой финишной чертой — загрузка резервной копии, получение удалённого обновления. Это не состояние покоя; начиная с Android 15 сервисы dataSync и mediaProcessing получают накопительный бюджет времени работы примерно в шесть часов в скользящем 24-часовом окне. Превысите его — и система вызовет Service.onTimeout(), дав короткую отсрочку на завершение работы перед принудительной остановкой сервиса. Это разумный компромисс для того, для чего и создан этот тип, — передачи, которая должна закончиться, — и плохой выбор для чего-то, что задумано работать бессрочно.

specialUse: тип для всего, чего нет в списке

25-минутный таймер фокусировки — это не синхронизация данных, не воспроизведение медиа и не привязка к датчику. Это ровно тот пробел, который призван закрыть specialUse — foreground-сервис, который действительно ограничен по времени и запущен пользователем, но не подходит ни под одну из именованных категорий. Загвоздка в том, что просто объявить его и забыть не получится: тег <property> в манифесте требует строку с подтипом, описывающую, чем занимается сервис, и проверка приложения в Google Play читает эту строку. Объявите specialUse для чего-то, что на деле является замаскированной синхронизацией данных или хаком для поддержания процесса живым, — и именно такое несоответствие будет отмечено при проверке, а не после запуска.

Для Mintly подтип — active-focus-timer, и форма декларации foreground-сервиса в Play Console формулирует то же самое простым языком: идущая сессия, которую пользователь запустил и активно наблюдает, завершающаяся в конкретный момент. Это легко обосновать, потому что это правда. Написать честное однострочное описание того, чем занимается сервис, — куда более разумная трата времени, чем попытки найти тип, который избежит пристального внимания проверки.

shortService: создан для короткого рывка, а не для сессии

Другой тип, который стоит знать, — shortService, предназначенный для foreground-сервиса, которому нужно всего пара минут, чтобы что-то завершить — записать большой экспорт, выполнить разовую очистку, — то есть меньше времени, чем пользователи готовы терпеть холодный старт ускоренного задания WorkManager. У него жёсткий лимит времени работы около трёх минут; система не идёт на уступки сверх этого, и тип существует именно для того, чтобы короткие задачи перестали злоупотреблять dataSync или specialUse ради большей свободы, чем им нужно. Если ваша задача предсказуемо завершается меньше чем за три минуты, это честный тип для использования. Если она иногда затягивается, это неправильный выбор, и вы узнаете об этом в продакшене, а не на тестах.

Ошибиться с типом — это баг продакшена, а не предупреждение линтера

Тип, который вы объявляете, — не метаданные, которые Android вежливо проигнорирует, если вы не уверены. Объявите dataSync для чего-то, что работает по восемь часов в день, — и его прервут по таймауту прямо посреди работы, и приложению придётся корректно это обнаружить и восстановиться, иначе пользователь просто увидит зависшую функцию без объяснений. Объявите specialUse без честного подтипа — и проверка Play может попросту отклонить обновление, а это куда более серьёзная задержка запуска, чем те десять минут, которые нужны, чтобы сразу выбрать правильный тип.

Решение сводится к двум вопросам: соответствует ли этот сервис реальной возможности вроде camera или mediaPlayback? Если нет — ограничена ли работа по времени естественным образом минутами (shortService), часами с чётким концом (dataSync), или она открытая, но запущена пользователем и активно отслеживается (specialUse, честно объявленный)? Ответьте на это до того, как напишете запись в манифесте, а не после того, как придёт отчёт о сбое. Систему типов неприятно изучать один раз, а потом это просто факт о платформе — точно так же, как Live Updates в Android 16 ложатся поверх сервиса таймера Mintly, а не заменяют необходимость с самого начала правильно выбрать тип его foreground-сервиса.

// По теме

Ещё из журнала

MFKAPPS 4 мин чтения

Разработка Mintly: как удержать точность таймера фокуса, когда Android хочет убить процесс

У работающего таймера Pomodoro проблема с надёжностью сложнее, чем у одноразового напоминания. Вот как Mintly переживает режим Doze, гибель процесса и дрейф при выключенном экране благодаря foreground-сервису и времени окончания по системным часам.

#android #engineering #mobile
MFKAPPS 4 мин чтения

In-App Review API в Android: как просить оценку, не раздражая пользователя

Практическое руководство по In-App Review API от Google — как он на самом деле работает, когда его запускать и почему обычный попап «оцените нас» незаметно портит вашу оценку в Play Store.

#android #engineering #kotlin
MFKAPPS 5 мин чтения

Ярлыки приложений Android и плитка быстрых настроек: логирование воды без открытия приложения

Практическое руководство по динамическому ShortcutManager API и TileService в Android — как позволить действию в один тап полностью обойти приложение, и о подводных камнях, на которых спотыкается большинство реализаций.

#android #engineering #kotlin