Чем отличаются Context, Application и Service и когда что использовать

Короткий ответ: для UI и всего, что связано с окном/темой, используйте context Activity/Fragment; для долгоживущих singletons и системных API — applicationContext; для задач, которые должны работать после закрытия экрана, — Service (обычно foreground), но сначала рассмотрите WorkManager.

Context — что это и как выбирать

Context — интерфейс доступа к ресурсам и системным сервисам: ресурсы, файлы, getSystemService(), запуск компонентов. В Android его реализуют Activity, Service и Application, а также обёртки (ContextWrapper).

Практические правила:

  • UI (диалоги, темы, навигация) — Activity/Fragment context: this, requireContext(), requireActivity().
  • Долговечные объекты (репозитории, кеши, DB) — applicationContext.
  • Вью берет свой context, но не храните Activity в статических полях.

Неправильный выбор Context — частая причина утечек памяти и исключений WindowManager$BadTokenException.

Application — глобальная инициализация

Application создаётся один раз на запуск процесса и живёт до завершения. Это Context без UI, удобный для инициализации и хранения компонентов, которые должны жить на весь процесс.

Когда использовать:

  • Инициализация DI, логирования, баз данных.
  • Передача applicationContext в singletons.
  • Хранение кешей и глобальных менеджеров.

Пример (Kotlin):

class App : Application() {
  override fun onCreate() {
    super.onCreate()
    initDI()
    initLogger()
  }
}

Когда не использовать:

  • Для построения диалогов, PopupMenu, тем — нужен Activity context.
  • Не используйте applicationContext для операций, завязанных на окно/тему.

Не прокидывайте applicationContext в адаптеры для построения диалогов или в навигацию — это приведёт к ошибкам и плохому UX.

Service — для настоящих фоновых задач (и альтернативы)

Service — компонент без UI, реализует Context, но выполняется в UI-потоке по умолчанию. Используйте Service, когда задача должна продолжаться после закрытия Activity (музыка, геотрекинг). Для коротких или отложенных задач чаще подойдёт WorkManager или корутина в ViewModel.

Ключевые моменты:

  • Service не создаёт новый поток — вы сами переносите работу в корутину/поток.
  • Для долгих задач, требующих видимости пользователю, используйте foreground service с уведомлением.
  • Для отложенной/периодической работы предпочитайте WorkManager (с учётом ограничений Doze).

Пример стартового Service:

class MyService : Service() {
  override fun onBind(intent: Intent?) = null
  override fun onStartCommand(intent: Intent?, flags: Int, startId: Int): Int {
    CoroutineScope(Dispatchers.Default).launch { doWork() }
    return START_STICKY
  }
}

Частые ошибки

  • Хранение Activity/Fragment в singletons -> утечки памяти.
  • Использование applicationContext для UI-операций -> BadTokenException/неправильная тема.
  • Запуск Service для коротких фоновых операций вместо WorkManager -> лишний расход батареи.
  • Предположение, что Service выполняется в отдельном потоке.

FAQ

  • Нужно ли всегда использовать WorkManager вместо Service?
    • Нет. Для длительных задач, связанных с видимой нотификацией/аудио/трекингом лучше Service (foreground). Для отложенной/периодической работы — WorkManager.
  • Можно ли хранить DB-инстанс в Application?
    • Да. Инициализируйте в Application и используйте applicationContext при создании.
  • Как избежать утечки при передаче Context в библиотеки?
    • Передавайте applicationContext для долгоживущих объектов; для UI передавайте Activity/Fragment и не сохраняйте их в static.

Хорошее практическое правило: всё, что может пережить экран — привязывайте к applicationContext; всё, что связано с окном, темой и жизненным циклом — к Activity/Fragment; если задача должна выполняться без UI — подумайте Service, но сначала проверьте WorkManager.