Context, Intent и четыре компонента приложения

Три вопроса, за которыми стоит одна тема: как ваш код разговаривает с системой и с другими приложениями.

Context — точка доступа к среде приложения: ресурсам, системным сервисам, файлам, запуску компонентов. Не «god object», а интерфейс к Android для конкретного участка кода.

Вопрос 1: «Что такое Context и какие виды бывают?»

Что проверяет интервьюер

Понимаете ли вы, что разные Context имеют разное время жизни. Из этого напрямую растёт самая частая утечка памяти в Android-приложениях.

Развёрнутый ответ

Практически различают два вида:

  • Application context — живёт столько же, сколько процесс. Получается через context.applicationContext. Годится для того, что переживает экраны: синглтоны, база данных, DI-граф, WorkManager.
  • Activity context — живёт ровно столько, сколько экран. Знает про тему оформления и про окно. Нужен для инфляции разметки, диалогов, запуска новых экранов из UI.

Кроме них есть Service context, context у BroadcastReceiver (ограниченный: из него нельзя показывать UI и регистрировать приёмники) и ContextThemeWrapper, который оборачивает другой context, подменяя тему.

// УТЕЧКА: синглтон переживёт Activity и удержит её вместе со всеми View
object Analytics {
    lateinit var context: Context   // сюда положили Activity — утечка на весь процесс

    fun init(context: Context) {
        this.context = context
    }
}

// Правильно: явно требуем Application context
class Analytics(private val app: Application) { /* ... */ }

// или так, если конструктор принимает произвольный Context
class Analytics(context: Context) {
    private val appContext = context.applicationContext
}

Обратное правило тоже важно: Application context нельзя использовать где попало. Диалог требует Activity — ему нужен window token и тема. Попытка показать AlertDialog с application context даёт BadTokenException. Инфляция разметки с application context «работает», но берёт системную тему вместо вашей — и вёрстка выглядит поплывшей.

Ещё частый вопрос: почему startActivity с application context падает? Потому что у него нет задачи (task), в которую поместить новый экран — нужен флаг FLAG_ACTIVITY_NEW_TASK, и это, как правило, признак того, что архитектура пошла не туда.

Вопрос 2: «Explicit и implicit Intent — в чём разница?»

Что проверяет интервьюер

Знаете ли вы, что Intent — это не «способ открыть экран», а сообщение системе, которое она маршрутизирует.

Развёрнутый ответ

Explicit intent называет получателя по имени класса. Так вы открываете свои экраны и запускаете свои сервисы. Implicit intent описывает намерение — action, category, data — а получателя ищет система среди приложений с подходящим intent-filter.

// explicit — знаем класс получателя
startActivity(Intent(this, DetailsActivity::class.java).apply {
    putExtra("itemId", 42L)
})

// implicit — описываем намерение, получателя выберет система
val intent = Intent(Intent.ACTION_VIEW, Uri.parse("https://codechick.io"))
if (intent.resolveActivity(packageManager) != null) {
    startActivity(intent)
}
<activity android:name=".DeepLinkActivity" android:exported="true">
    <intent-filter>
        <action android:name="android.intent.action.VIEW" />
        <category android:name="android.intent.category.DEFAULT" />
        <category android:name="android.intent.category.BROWSABLE" />
        <data android:scheme="https" android:host="codechick.io" />
    </intent-filter>
</activity>

Что добавит вам очков на middle-собеседовании:

  • Начиная с Android 12 любой компонент с intent-filter обязан явно объявлять android:exported, иначе приложение не установится.
  • Начиная с Android 11 resolveActivity и queryIntentActivities видят не все приложения — работает package visibility. Нужный пакет надо объявить в секции <queries> манифеста, иначе система «не найдёт» получателя.
  • PendingIntent — это не Intent, а токен, отдающий чужому процессу право выполнить действие от вашего имени (уведомления, виджеты, алармы). Начиная с Android 12 обязателен флаг FLAG_IMMUTABLE или FLAG_MUTABLE; по умолчанию берите immutable — это вопрос безопасности.

Вопрос 3: «Назовите компоненты приложения»

КомпонентЗачемЧто спросят дополнительно
Activityэкран с UIжизненный цикл, task и back stack, launch modes
Serviceработа без UIчем foreground service отличается от обычного, почему фоновая работа сегодня — это WorkManager
BroadcastReceiverреакция на системные событияманифест против регистрации в коде, лимиты неявных broadcast с Android 8, флаг exported с Android 13
ContentProviderотдача данных другим приложениямзачем нужен, если внутри приложения хватает Room

Все четыре объявляются в AndroidManifest.xml (кроме приёмников, зарегистрированных в коде). Важный современный акцент: Service для отложенной фоновой работы почти всегда неверный выбор — система его убьёт. Правильный ответ — WorkManager, а foreground service — только для того, что пользователь видит прямо сейчас (навигация, плеер, запись трека).

Типичные ошибки кандидатов

  • «Context нужен, чтобы получить ресурсы» — верно, но не отвечает на вопрос о видах и времени жизни.
  • Хранят Activity в статических полях или синглтонах — самая частая утечка.
  • Путают Service и поток: сервис по умолчанию работает на главном потоке и сам по себе ничего не распараллеливает.
  • Считают PendingIntent обычным интентом с задержкой.

Как ответить кратко (20–30 секунд)

«Context — доступ к ресурсам, сервисам и запуску компонентов. Различаю два по времени жизни: application context живёт весь процесс и годится для синглтонов и БД, activity context живёт столько же, сколько экран, знает тему и окно, поэтому нужен для диалогов и инфляции. Держать activity context в синглтоне — классическая утечка. Intent бывает explicit — с конкретным классом получателя, и implicit — описывает action и data, а получателя система ищет по intent-filter; с Android 11 тут ещё работает package visibility. Компонентов четыре: Activity, Service, BroadcastReceiver, ContentProvider, все объявляются в манифесте, и для отложенной фоновой работы сегодня берут WorkManager, а не Service.»

Проверьте себя
1. Синглтон хранит переданный ему Context в поле. Какой Context туда безопасно класть?
Aлюбой — сборщик мусора разберётся сам
BActivity context, чтобы иметь доступ к теме оформления
CapplicationContext, потому что его время жизни совпадает с процессом
DContext из BroadcastReceiver, он самый лёгкий
2. Что делает implicit intent принципиально отличным от explicit?
Aон не требует объявления в AndroidManifest.xml
Bон описывает намерение (action, category, data), а получателя выбирает система по intent-filter
Cон всегда запускает компонент в отдельном процессе
Dон работает только для BroadcastReceiver, а для Activity нужен explicit
3. Приложению нужно раз в сутки синхронизировать данные с сервером, даже если пользователь закрыл приложение. Что выбрать?
Aобычный Service, запускаемый из Application.onCreate
Bforeground service с постоянным уведомлением
CWorkManager с периодическим запросом и ограничениями
Dпоток, запущенный в BroadcastReceiver на BOOT_COMPLETED