Урок 2: ПОЧЕМУ ОН (Jetpack Compose), а не XML
Оглавление:
Введение
Давайте пройдемся по теории. В чем идея, задумка, зачем 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, всю мощь и прелесть вы ощутите когда начнете разрабатывать на нем.
Бесплатные Telegram-боты для обучения
Практика с проверкой кода и помощью ИИ-ментора
AndroidSprint AI Mentor
Проверяет Pull Request'ы в GitHub, проводит тестовые собеседования с голосом и таймером, помогает разбираться с кодом 24/7
Попробовать ИИ-ментора →KotlinSprint Bot
22 урока Kotlin, 220 тестов, 120 практических задач с код-ревью
Начать обучение Kotlin →