РозробкаБудь-який рівень

Android / Kotlin

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

Час життя
Належить системі
Мова
Kotlin, на JVM
Інтерфейс
Compose, під ним View

Системі належить ваш екран

Поверніть пристрій — і Android знищує вашу активність та будує нову. Так само роблять зміна мови, зміна розміру вікна в режимі поділу екрана, перемикання теми чи підключення клавіатури. Об’єкт, який тримав ваш код, зник, і той самий екран виконує новий екземпляр. Кожен новачок в Android зустрічає це однаково: як форму, що спорожнюється, щойно хтось уперше поверне телефон боком.

Активність Android знищують і будують заново значно частіше, ніж очікують: це робить поворот екрана, а також зміна мови, зміна розміру вікна чи перемикання теми. Окремо система може вбити весь процес, поки застосунок у фоні, і відбудувати його, коли користувач повернеться. Три рівні внизу — це практична відповідь: ViewModel переживає перестворення, але гине з процесом, збережений стан переживає обидва, але тримає лише малі значення, а все переживає тільки справжнє сховище.

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

Та сама логіка керує фоновою роботою. Застосунок, якого немає на екрані, не має права далі працювати, і платформа затягувала це з кожним релізом: обмеження фону, режим doze і виробники, що додають власні обмеження зверху. Робота, яка колись має статися, іде у WorkManager: він зберігає її та виконує, коли система дозволить, переживаючи і смерть процесу, і перезавантаження. Робота, яку має бути видно, поки вона триває, потребує сервісу переднього плану й сповіщення, бо користувач має право знати.

Kotlin і зокрема корутини

Kotlin тепер і є мовою Android: рекомендація Google, мова, якою написано документацію, і те, під що спроєктовані всі сучасні API. Він працює на JVM і напряму викликає Java-бібліотеки, тож впровадження в наявну Java-базу відбувається файл за файлом, а не все одразу. Дві його риси виправдовують себе одразу: можливість null є частиною системи типів, тож значення, яке може бути null, не можна використати так, ніби воно не може; а класи даних прибирають шаблонний код, що робив Java-моделі виснажливими.

Корутини — це та частина, що змінює саме письмо Android-коду. Функція, що призупиняється, може зупинитися, не блокуючи свого потоку, тож асинхронна робота читається згори вниз, а не як вкладені колбеки. Поняття, яке робить їх безпечними, а не лише зручними, — це структурована паралельність: кожна корутина належить до області, і коли область скасовують, скасовуються й усі корутини всередині. Запустіть роботу в viewModelScope — і вона зупиниться автоматично, коли ViewModel зникне: витік фонової задачі, що оновлює вже неіснуючий екран, тут спроєктовано геть, а не тримають у пам’яті.

Поверх корутин лежить Flow — потік значень у часі, природна форма для запиту до бази, що має перевидавати результат при зміні даних, або для поля пошуку, що має запитувати, поки користувач набирає. Саме для стану екрана StateFlow тримає поточне значення й видає оновлення, — а це рівно те, що потрібно інтерфейсу: щось, що можна прочитати негайно, коли екран перебудували, і спостерігати за змінами далі.

Compose і система View

Jetpack Compose — сучасний і рекомендований набір інструментів для інтерфейсу: ви пишете композовані функції, що описують інтерфейс для заданого стану, а фреймворк перескладає те, що змінилося. Він замінює XML-розкладки, findViewById, адаптери й увесь апарат тримання посилань на в’ю та ручного їх змінювання: для списку це різниця між адаптером RecyclerView із view holder і LazyColumn із лямбдою.

АспектView (XML)Compose
Як стан доходить до UIВи знаходите в’ю, ставите значення й самі тримаєте їх синхронними.Інтерфейс є функцією стану; синхронізувати нічого.
СпискиRecyclerView, адаптер і view holder — кілька файлів на список.LazyColumn з лямбдою елемента, віртуалізований від початку.
Де кусаєВитеклі посилання й стан, що розходиться з екраном.Нестабільні параметри спричиняють перескладання того, що не змінювалося.
Реальність у 2026Більшість наявних екранів і чимало бібліотек, які його досі очікують.Куди йде нова робота; обидва взаємодіють в обидва боки.

Патерн, який документація платформи тепер припускає і який варто взяти незалежно від набору інструментів: стан тече вниз, події течуть угору. Композована функція отримує свій стан параметром і повідомляє про події лямбдами, не тримаючи власного стану там, де може цього уникнути. Це робить її придатною для прев’ю, тестування й перевикористання — і зводить кожне рішення про те, що має показати екран, в одне місце, у ViewModel, яке до того ж є єдиним, що переживає те перестворення, з якого починалася ця сторінка.

Як це виявляється в реальній поставці

Фрагментація реальна, але не така, як її уявляють. Проблему рівнів API здебільшого розв’язано: бібліотеки підтримки переносять більшість назад, а minSdk — це число, яке ви обираєте за даними власних користувачів. Насправді різниться залізо під сподом: телефон із двома гігабайтами пам’яті й повільним процесором швидше вб’є ваш процес, згубить кадри, яких не губить ваш тестовий пристрій, і виявить кожне виділення пам’яті в обробнику скролу. Тестуйте на дешевому пристрої, бо на такому сидить помітна частка ваших користувачів.

Реліз тут поблажливіший, ніж на iOS, в одному й суворіший в іншому. Рев’ю Google Play зазвичай швидше, а поетапне розгортання дозволяє випустити на один відсоток користувачів, подивитися частоту падінь у консолі й зупинитися, не випускаючи нової збірки. Натомість дотримання політик автоматизоване й несентиментальне: дедлайни цільового рівня API, декларації дозволів і форми безпеки даних, що мають збігатися з тим, що застосунок реально робить. Обидва останні — календарні пункти, а не інженерні, і саме про них команди забувають.

Де це вироджується

  • Стан екрана, що живе в активності, спорожнюється при першому ж повороті пристрою.
  • Нічого не записано у збережений стан — і застосунок повертається порожнім після вбивства процесу.
  • Контекст активності, який тримає синглтон чи статичне поле, тече разом з усією ієрархією в’ю.
  • Фонова робота, запущена поза областю життєвого циклу, працює далі, не маючи куди віддати результат.
  • Відкладена робота в сервісі замість WorkManager гине разом із процесом.
  • Дозволи, запитані на старті, а не в контексті, привчають користувачів відмовляти.
  • Тестування лише на флагмані ховає всі проблеми з пам’яттю й кадрами, які є у ваших користувачів.

Коли це застосовувати

Застосовуйте, коли

  • Продукти, чиї користувачі здебільшого на Android: у більшій частині світу це переважна більшість.
  • Застосунки з глибокою інтеграцією в систему: фонова синхронізація, віджети, шеринг, власні клавіатури, сервіси доступності.
  • Усе, що має прийнятно працювати на слабкому залізі, де в нативної збірки є запас, якого немає в альтернатив.
  • Команди, що вже на JVM: Kotlin — коротка дистанція, а інструментарій буде знайомим.

Уникайте, коли

  • Будувати обидві платформи нативно однією малою командою: поверхня подвоюється, а людей не більшає.
  • Дизайни з розрахунком на фіксований розмір екрана: тут є всі розміри, і життєвий цикл реагує на кожен.
  • Архітектури, що припускають живий процес, — саме цього припущення Android і не шанує.
  • Продукти, які не можуть щороку підіймати цільовий рівень API: тут це жорсткий дедлайн.

Було корисно?

Поділіться з тим, хто працює над тією ж задачею.