AI
prepair.app
Пройти інтерв'ю →
EnglishУкраїнськаРусский
🤖

Питання на співбесіді Android розробника

Співбесіда Android-розробника крутиться навколо ідіом Kotlin, корутин і життєвого циклу платформи — більшість senior-питань насправді перевіряють, чи розумієте ви, що процес можуть убити будь-якої миті. Нижче — питання, які ставлять найчастіше, кожне з еталонною відповіддю.

Junior · без досвіду / до 1 рокуMiddle · 2–4 роки досвідуSenior · 5+ років досвіду

Що запитують

Можливості мови Kotlin
Корутини та Flow
Життєвий цикл Android
Jetpack Compose
Архітектура і DI
Room, Retrofit і тести

12 реальних питань з відповідями

До кожного питання — еталонна відповідь, з якою можна порівняти свою.

1

Як працює null safety у Kotlin і чим небезпечні platform types?

Відповідь

Kotlin ділить кожен тип на nullable (String?) і non-nullable (String), і компілятор не дає звернутися до nullable-значення без перевірки, тому NullPointerException здебільшого зникає. Platform types зʼявляються на межі з Java: Kotlin не знає, чи може Java-метод повернути null, і довіряє вам. Саме звідти NPE і приходять — анотуйте Java-API або вважайте їхній результат nullable у місці виклику.

2

Що дають data class і sealed class?

Відповідь

Data class генерує equals, hashCode, toString, copy і деструктуризацію за первинним конструктором, тому це правильний інструмент для значень, які порівнюють і копіюють, — моделі API, стан UI. Sealed class обмежує набір нащадків відомим списком, тому when по ньому вичерпний і компілятор вкаже на забуту гілку. Разом вони чисто моделюють стан екрана: sealed-інтерфейс Loading, Content, Error, а дані несе data class.

3

Що таке структурна конкурентність у корутинах і навіщо потрібен scope?

Відповідь

Структурна конкурентність означає, що кожна корутина належить скоупу і батьківській задачі, тому скасування батька скасовує всіх дітей, а батько не завершиться, доки не завершаться вони. Саме це рятує від фонової роботи, що витекла, характерної для голих потоків. В Android viewModelScope скасовується при очищенні ViewModel, а lifecycleScope — при завершенні життєвого циклу, тож робота зупиняється, коли її результат уже нікуди застосувати.

4

Поясніть диспетчери і що для чого використовується.

Відповідь

Dispatchers.Main працює на UI-потоці, там торкаються вʼю. Dispatchers.IO — великий еластичний пул для блокуючої роботи: диск, мережа, база, — бо такі потоки здебільшого чекають. Dispatchers.Default розміром з кількість ядер і призначений для справжніх обчислень на кшталт парсингу чи сортування. Блокуючий виклик на Default виїдає пул — це найчастіша помилка з диспетчерами.

5

У чому різниця між Flow, StateFlow і LiveData?

Відповідь

Flow — холодний асинхронний потік: він починає видавати значення при підписці, і в кожного підписника своє виконання. StateFlow гарячий і завжди тримає рівно одне поточне значення, що робить його природним для стану UI — новий підписник одразу отримує актуальний стан. LiveData — старіший lifecycle-aware тримач; StateFlow разом із repeatOnLifecycle закриває ту саму задачу рештою інструментарію корутин, тому новий код зазвичай обирає його.

6

Чому зміна конфігурації перестворює Activity і як це пережити?

Відповідь

Поворот, темна тема, зміна мови й розміру вікна знищують і перестворюють Activity, щоб вона підвантажила ресурси під нову конфігурацію. Стан, який має вижити, живе у ViewModel — вона переживає перестворення. Стан, який має пережити ще й смерть процесу, іде в SavedStateHandle, бо систему ніхто не зобовʼязує тримати ваш процес у фоні. Перевірка цього шляху через «Не зберігати активності» відрізняє надійний застосунок від того, що втрачає введені користувачем дані.

7

Що спричиняє рекомпозицію в Jetpack Compose і через що вона гальмує?

Відповідь

Compose перезапускає composable, коли змінюється прочитаний ним State, і пропускає ті, у яких параметри рівні та стабільні. Гальмування йде від нестабільних параметрів — звичайний List або лямбда, що захопила змінюване значення, — вони ламають пропуск і перезапускають великі піддерева. Лікується підняттям стану до найближчого спільного власника, незмінними колекціями чи анотацією @Immutable, а також відкладеним читанням через лямбди, щоб перезапускалася лише фаза layout або draw.

8

Навіщо впровадження залежностей через Hilt замість прямого створення обʼєктів?

Відповідь

DI позбавляє клас знання про те, як зібрати свої залежності, що робить його тестованим і тримає проводку в одному місці замість розкиданих сінглтонів. Hilt генерує граф Dagger і привʼязує час життя компонентів до андроїдних: @Singleton живе із застосунком, @ViewModelScoped — з ViewModel. Практична вигода — підмінити справжній репозиторій фейком у тестах, не чіпаючи продакшен-код.

9

Як влаштований екран з точки зору чистої архітектури?

Відповідь

Шар UI тримає Compose або View плюс ViewModel, яка віддає незмінний стан і приймає події. Шар домену тримає юзкейси і чисті бізнес-правила без імпортів Android — саме тому він тестується звичайним JUnit. Шар даних тримає репозиторії, які обирають між Room і Retrofit та мапять DTO у доменні моделі. Важливий тут напрям залежностей: домен нічого не знає ні про дані, ні про UI.

10

Через що виникає ANR і як знайти його вже після релізу?

Відповідь

ANR спрацьовує, коли головний потік заблокований приблизно на пʼять секунд на введенні, а для бродкастів і сервісів ліміти жорсткіші. Типові причини — звернення до бази чи мережі на головному потоці, величезний синхронний розбір JSON, конкуренція за блокування з фоновим потоком. Після релізу звіти ANR у Play Console разом зі знятим стеком головного потоку вказують на блокуючий кадр; StrictMode у debug-збірках ловить більшу частину цього задовго до релізу.

11

Як уникати витоків памʼяті в Android?

Відповідь

Повторювана причина — щось довгоживуче тримає Activity або View: статичне посилання, невідписаний слухач, внутрішній клас із неявним посиланням на зовнішній, корутина на GlobalScope. Захист — привʼязувати роботу до lifecycle-aware скоупу, обнуляти посилання на binding у onDestroyView у фрагментів і використовувати Context застосунку там, де не потрібен Context активності. Решту ловить LeakCanary у debug-збірці, показуючи ланцюжок утримання.

12

Що таке scope-функції і коли яка читається краще?

Відповідь

let приймає отримувача аргументом і повертає результат лямбди — підходить для перевірки на null і перетворення. run робить те саме, але через this, зручний для блоку викликів, що повертає значення. apply повертає самого отримувача і потрібен для налаштування обʼєкта. also теж повертає отримувача, але передає його як it, тому годиться для побічних ефектів на кшталт логування. with — не розширення і просто групує виклики. Вибір за поверненим значенням і формою отримувача якраз і тримає їх читаними, а не хитрими.

🦎

Прочитати відповіді недостатньо

На співбесіді ти говориш вголос під тиском. Кем поставить ті самі питання, оцінить кожну відповідь і покаже, що саме підтягнути.

Пройти інтерв'ю Android (Kotlin) →
Безкоштовно · 3 інтерв'ю на місяць

Інші спеціалізації

🔍QA Manual🤖QA AutomationJava Backend🐍Python Backend🐘PHP Backend🦫Go Backend🟢Node.js Backend⚛️React Frontend💚Vue Frontend🅰️Angular FrontendNext.js🍏iOS (Swift)⚙️DevOps / SRE🗄️Data Engineer📈Business Analyst🎯Product Manager📋Project Manager🎨UI/UX Designer📣Marketing