Добавить в корзинуПозвонить
Найти в Дзене

Урок 2: ПОЧЕМУ ОН (Jetpack Compose), а не XML | Курс Android Jetpack Compose

Смотреть на обучающей платформе с ИИ: https://androidsprint.ru/courses/jetpack-compose-osnovy Смотреть в VK:
https://vkvideo.ru/video-85562117_456239137?pl=-85562117_3 Смотреть на YouTube:
https://www.youtube.com/watch?v=QEK0FnRkIR8 Давайте пройдемся по теории. В чем идея, задумка, зачем Jetpack Compose вообще появился и почему все так хотят сейчас писать на нем. Я расскажу очень поверхностно про историю разработки на XML, попутно обращая внимания на нововведения и неудобства этого подхода. А в конце мы подведем итоги, сравнив все с Compose, чтобы была наглядна видна эволюция развития подхода к разработке. Чтобы для вас стали очевидны все преимущества декларативного фреймворка. Тогда вы полюбите его еще больше. Раньше для создания пользовательского интерфейса мы использовали XML-файлы, в которых описывали большую часть компонентов — кнопки, текстовые поля, контейнеры и так далее. Верстка интерфейсов была на языке разметки XML в виде вложенной структуры данных. В целом это было удобно
Оглавление
Смотреть на обучающей платформе с ИИ: https://androidsprint.ru/courses/jetpack-compose-osnovy
Смотреть в VK:
https://vkvideo.ru/video-85562117_456239137?pl=-85562117_3
Смотреть на YouTube:
https://www.youtube.com/watch?v=QEK0FnRkIR8

Введение

Давайте пройдемся по теории. В чем идея, задумка, зачем Jetpack Compose вообще появился и почему все так хотят сейчас писать на нем.

Я расскажу очень поверхностно про историю разработки на XML, попутно обращая внимания на нововведения и неудобства этого подхода. А в конце мы подведем итоги, сравнив все с Compose, чтобы была наглядна видна эволюция развития подхода к разработке. Чтобы для вас стали очевидны все преимущества декларативного фреймворка. Тогда вы полюбите его еще больше.

Как создавали интерфейс раньше?

Раньше для создания пользовательского интерфейса мы использовали XML-файлы, в которых описывали большую часть компонентов — кнопки, текстовые поля, контейнеры и так далее. Верстка интерфейсов была на языке разметки XML в виде вложенной структуры данных. В целом это было удобно для представления UI, который в своей сути тоже имеет вложенную структуру.

Базовые классы

Проектирование интерфейса на XML было похоже на создание дерева. Android сам парсил XML-файлы и создавал иерархию объектов View в памяти, которое отражало структуру интерфейса.

Разработчики Android представляли каждый элемент в виде класса. Классы были созданы для базовых компонентов, таких как View или ViewGroup (и их потомков), и выстроили их в иерархию. Например, каждый элемент экрана — кнопка, текстовое поле или изображение – наследуется от базового класса View, а контейнеры от ViewGroup.

Также разработчики могли визуально рисовать интерфейсы, а затем видеть их в визуальном редакторе. То есть, ты пишешь код, и сразу на превью видишь, как интерфейс выглядит. Такой подход называется WYSIWYG — what you see is what you get. Качество таких превью, безусловно, оставляет желать лучшего. Но это лучше, чем ничего.

Надо сказать, что базовые классы и их наследники огромны. Они перегружены логикой и имеют сложную иерархию. Их довольно тяжело поддерживать и расширять, особенно при создании кастомных компонентов. Это усложняет процесс разработки и приводит к увеличению часов на разработку приложения.

Перерисовка компонентов

Естественно система должна всю это иерархию как-то отображать на экране. Перерисовка интерфейса могла существенно влиять на производительность.

Отрисовка осуществляется самой платформой. Мы только строим это дерево и можем взаимодействовать с ним, вызывая функции классов для изменения каких-либо состояний.

При изменении параметров приходится перегруппировывать элементы, выполнять новый расчет размеров и делать множественные вызовы метода measure.

Эффективность здесь зависит от того, насколько качественно был реализован процесс верстки, глубина вложенности контейнеров и тому подобное. И поскольку мы работаем с XML, неизбежна стадия инфлейтинга.

Управление состоянием

Окей, и с этим научились жить. Количество кода росло, логика усложнялась. В первые годы разработчики применяли императивный подход к изменению интерфейса. Изменения UI происходили напрямую в разных частях кода. А заниматься хранением состояния и централизованно им управлять начали значительно позже появления самой платформы.

Понятие состояния как единого «источника правды» (source of truth) начало набирать популярность с появлением архитектурных паттернов, таких как MVP и позже MVVM.

То есть в какой-то момент разработчики начали понимать преимущества подхода, где используется стейт (состояние), который отрисовывается в UI. Мы просто меняем состояние, а UI автоматически обновляется – вспоминаем проходимый ранее механизм подписки через Observer.

Например, у нас есть UI-стейт во ViewModel в виде LiveData или в последнее время чаще StateFlow, мы берем, подписываемся и полностью отображаем его в интерфейсе. Это удобно: мы просто присваиваем значения элементам UI, не смешивая логику и отрисовку.

Реактивное программирование

Возвращаясь к Compose, важно упомянуть, что этот декларативный фреймворк сочетает в себе помимо лаконичного языка, еще и модель реактивного программирования. Вся архитектура Compose построена на принципах реактивности. Реактивное программирование — это подход, при котором система автоматически реагирует на изменения данных, вместо того чтобы вручную проверять их состояние.

В Compose этот принцип реализуется через декларативный подход: ты описываешь, как должен выглядеть UI в зависимости от состояния данных, а сама система берет на себя заботу о том, чтобы обновить интерфейс при изменении этих данных. Нет необходимости вручную подписываться на изменения с помощью LiveData или StateFlow — всё работает автоматически. Когда данные изменяются, компоненты интерфейса перерисовываются только тогда, когда это необходимо, а не при каждом изменении состояния, как это было в старом императивном подходе.

Это и есть суть реактивного программирования, которая лежит в основе Jetpack Compose. Это новый виток эволюции в Android по части работы с состоянием.

Проблемы с превью и временем разработки

Стейты сильно упростили всем разработчикам жизнь. Но всё равно верстать в XML все еще остается довольно неудобно.

  • Превью часто не соответствует тому, что ты рисуешь. То есть то, что ты видишь, не всегда будет выглядеть так же на устройстве.
  • В превью используются только mock-данные. Чтобы увидеть, как экран будет выглядеть с реальными данными, приходится собирать приложение и запускать его на устройстве.
  • Если нужно проверить какой-то экран, который находится глубоко в навигации, приходится ещё переходить между несколькими экранами. Или делать специальные debug-экраны, с которых осуществляется быстрый переход на разрабатываемый экран.

На первый взгляд это может показаться незначительным, но такие мелочи сильно замедляют разработку. Поэтому у разработчиков часто возникает недоверие к превью. Даже если оно отображает экран с вероятностью 90% правильно, всё равно приходится компилировать и проверять вручную. Иначе нельзя. А это занимает время. А время, как известно, — самый ценный ресурс программиста.

Как Jetpack Compose решает

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

  • Ну первое и очевидное это то, что необходимо знать дополнительный язык разметки. Compose естественно устраняет необходимость использовать XML для описания интерфейсов. Все пишется на Kotlin.
  • При работе с компонентами мы никак не связаны с громоздкими View-классами и сложными иерархиями. Каждый элемент интерфейса представлен как функция, которая описывает его внешний вид и поведение.
  • Compose изменяет интерфейс только тогда, когда изменяется состояние. Более того, перерисовывается только тот элемент на экране, который изменил свое состояние.
  • Кроме того, здесь нет этапа инфлейтинга, то есть все создается напрямую через методы. Это сокращает количество операций, связанных с преобразованием данных и существенно улучшает время отклика приложения, особенно при сложном UI.
  • Теперь что касается состояния, реактивщины и всего такого. В традиционном подходе мы создаем, например, LiveData. Затем создаем наблюдатель во фрагменте через него подписываемся на изменения этой переменной с “живыми данными”. Соответственно при обновлении LD или StateFlow внутри ViewModel – срабатывал наблюдатель и мы в нем перерисовывали UI. В Compose этот процесс значительно упрощается, становится более компактным. Вместо LiveData мы используем реактивные состояния. Главное отличие в том, что не нужно явно устанавливать Observer, подписываться и вручную прописывать методы обновления соответствующих View.

Что это такое и как это работает мы обязательно препарируем на уроках подробно.

  • В Compose превью работает не только с “моками”, но и позволяет подключать реальные данные из других источников. Это значит, что ты сразу видишь, как экран будет выглядеть в реальном приложении, без необходимости собирать APK и запускать его на устройстве. Более того, можно создавать несколько вариантов одного экрана с разными состояниями.
  • Также мы можем мгновенно видеть изменения в интерфейсе без полной компиляции приложения. Это кардинально ускоряет процесс работы: поправил отступ или добавил элемент — и сразу увидел результат. При работе с XML такого добиться нельзя.

Заключение

В целом, если быть кратким, тут только основные тезисы в пользу Compose, всю мощь и прелесть вы ощутите когда начнете разрабатывать на нем.

Дополнительные материалы

🔹 Обучающая платформа с ИИ: https://androidsprint.ru

🔹 Интерактивный курс Kotlin Практикум: https://androidsprint.ru/kotlin-praktikum

🔹 Код из всех уроков в телеграм канале: https://t.me/ievetrov

🔹 Сообщество в VK: https://vk.com/ivan.ievetrov