- Performance comparison: building Android Layout with XML vs By Code vs Jetpack Compose
- Intro
- Benchmark
- Results
- Benchmarks for Huawei Y5 Prime 2018 ( HUAWEI DRA-LX2 ) :
- Conclusion
- Compose. Jetpack Compose
- Появление
- Преимущества
- Недостатки
- Декларативный стиль
- Что за Unidirectional Data Flow?
- Первое впечатление от Android Jetpack Compose
- Что такое Android Jetpack Compose?
- 1. Независимость от релизов Android
- 2. Весь API на Kotlin
- 3. Composable = Композитный: использование композиции вместо наследования
- 4. Однонаправленный поток данных
- 5. Улучшение отладки
- Хватит слов, покажите код
- 1. FrameLayout vs Wrap + Padding + Background
- 2. Вертикальный LinearLayout vs Column
- 2a. Отступы
- 2b. Интервалы
- 2c. Горизонтальный LinearLayout vs Row
- 2d. Gravity vs Alignment
- 2d. Замечание
- 3.Стили и темы
- Что посмотреть/почитать
- Выводы
Performance comparison: building Android Layout with XML vs By Code vs Jetpack Compose
May 13, 2020 · 5 min read
Intro
I have always wondered what is the difference between writing your activity layout in XML or by code programmatically, then jetpack compose came along, so I did a little experiment to test the time it takes each method to create the activity, and I would like to share the results with you.
Benchmark
I didn’t exactly know how to calculate the views creating time so I added multiple stop points to achieve it, as follow:
I took the start time at the very beginning of the activity.
Then added mul t iple stop points, the first one at the end of the onCreate function.
The second one on the start of the onResume function.
The third stop point was recorded on the ViewTreeObserver GlobalLayoutListener to make sure all views have done their measurements.
Note: For Compose this was not possible because we are not dealing with views anymore.
The fourth stop point was an extra check out of curiosity, took it on Window DevoceView.
The last stop point was when the activity receives the focus.
Results
For this, I tested each method alone for 10 runs then close the app entirely and test the other one, no recent apps in the background, no wifi ( in case some app used it, and made the CPU busy a bit ).
- Android Studio Arctic Fox 2020.3.1 beta 3
- Compose version : 1.0.0-rc01 ( Updated thanks to Callmepeanut contribution )
- Tested on Galaxy S8 ( SM-G950F, Android 9 ) and Huawei Y5 Prime 2018 ( HUAWEI DRA-LX2, Android 8.1 ).
Note: All the numbers are in milliseconds.
Benchmarks for Galaxy S8 ( SM-G950F ) :
Benchmarks for Huawei Y5 Prime 2018 ( HUAWEI DRA-LX2 ) :
Note: All done with the same identical UI:
Conclusion
Looking at the numbers there is no that big of a difference between XML and Code only layouts, compose on the other hand has some delays.
Putting into consideration the time I took to make the layouts, the XML took about 10
15 minutes to finish the whole thing, By code took me approximately 1 hour since this is my first time creating a layout from scratch programmatically, in time and some extension functions the process will be faster, for Jetpack compose it was a piece of cake, done the whole thing in a couple of lines, no adapter, no constraint to match, I see it as the winner in this category, I hope it gets better in term of performance as well.
Источник
Compose. Jetpack Compose
Благодаря стремительному развитию мобильной индустрии каждые несколько лет мы наблюдаем появления новых технических решений, призванных усложнить упростить жизнь разработчикам. Некоторые из них, не сыскав популярности у пользователей, остаются лишь частью истории, другие – плотно укореняются в повседневной разработке, становясь в определенной области стандартом де-факто.
Пожалуй, главным трендом мобильной разработки за последние несколько лет стал декларативный UI. Такое решение уже давно успешно применяется в веб и кроссплатформенных решениях и, наконец, добралось и до нативной разработки. На iOS существует SwiftUI (представленный на WWDC 2019), а на Android – Jetpack Compose (представленный месяцем ранее на Google I/O 2019). И именно о последнем мы сегодня и поговорим.
Примечание: в данной статье мы не будем рассматривать поэтапное создание первого проекта на Compose, так как этот процесс прекрасно описан в других материалах. Моя цель – лишь рассказать о преимуществах и недостатках, которые дает android-разработчикам переход на Jetpack Compose, а решение использовать или нет всегда остаётся за вами.
Появление
Официальная история Jetpack Compose начинается с мая 2019, когда он был представлен публике на конференции Google I/O. «Простой, реактивный и Kotlin-only» – новый декларативный фреймворк от Google выглядел как младший брат Flutter (который к тому моменту уже стремительно набирал популярность).
API design is building future regret
О недостатках текущего UI-фреймворка Android было сказано и написано уже достаточно большое количество раз. Проблемы с View-иерархией, зависимость от релизов платформы – наличие этих и множества других мелких недостатков в той или иной мере доставляли неудобства разработчикам, что и побудило компанию Google заняться разработкой нового фреймворка, способного решить все эти проблемы.
Преимущества
Итак, чем же хорош Jetpack Compose и, главное, чем он кардинально отличается от существующего на данный момент UI-фреймворка Android?
- Unbundled toolkit: JC не зависит от конкретных релизов платформы, а значит, забудем уже про Support Library.
- Kotlin-only: Больше не нужно переключаться между классами и xml-файлами – вся работа с UI происходит в одном Kotlin-файле.
- Композитный подход: Наследованию – нет, композиции – да. Каждый UI-компонент представляет собой обычную composable-функцию, отвечающую только за ограниченный функционал, т.е. без лишней логики. Никаких больше View.java на 30 тысяч строк кода.
- Unidirectional Data Flow: Одна из основополагающих концепций Jetpack Compose, о которой будет рассказано подробнее чуть ниже.
- Обратная совместимость: Для использования Compose не требуется начинать проект с нуля. Имеется возможность как его встраивания (с помощью ComposeView) в имеющуюся xml-вёрстку, так и наоборот.
- Меньше кода: Тут, как говорится, «лучше один раз увидеть, чем сто раз услышать». В качестве примера возьмём классическое сочетание компонентов – два поля ввода и кнопка подтверждения:
В реализации текущего UI-фреймворка вёрстка этих компонентов выглядит так:
В то же время, при использовании Jetpack Compose, решение будет выглядеть следующим образом:
Ну и напоследок – сравнительный результат:
Недостатки
Декларативный стиль
Отдельное внимание стоит уделить главной особенности Jetpack Compose – декларативному стилю создания UI. Суть подхода заключается в описании интерфейса как совокупности composable-функций (они же виджеты), которые не используют «под капотом» view, а напрямую занимаются отрисовкой на canvas. Для кого-то это минус, для других – возможность попробовать что-то новое. Так или иначе, к концепции «верстать UI кодом» нативному разработчику, не работавшему ранее с аналогичными технологиями (к примеру, Flutter или React Native), придётся привыкать.
Что за Unidirectional Data Flow?
В современном android-приложении UI-состояние меняется в зависимости от приходящих событий (нажатие на кнопку, переворот экрана и т.д.). Мы нажимаем на компонент, тем самым формируя событие, а компонент меняет свой state и вызывает callback в ответ. Из-за довольно тесной связи UI-состояния с View это потенциально может привести к усложнению поддержки и тестирования такого кода. К примеру, возможна ситуация, когда помимо внутреннего state компонента, мы можем хранить его состояние в поле (например во viewmodel), что теоретически может привести к бесконечному циклу обновления этого самого state.
Что же касается Jetpack Compose, то здесь все компоненты по умолчанию являются stateless. Благодаря принципу однонаправленности нам достаточно «скормить» модель данных, а любое изменение состояния фреймворк обработает за нас. Таким образом, логика компонента упрощается, а инкапсуляция состояния позволяет избежать ошибок, связанных с его частичным обновлением. В качестве примера возьмем уже рассмотренный ранее composable-код. Перед описание компонентов были определены две переменные:
Мы создаем два текстовых объекта, значения которых будем устанавливать полям ввода (логина и пароля) в качестве value. А благодаря связке remember любое изменение значений этих объектов (из других частей кода) уведомит об этом соответствующее поле ввода, которое перерисует только значение value, вместо полной рекомпозиции всего компонента.
Источник
Первое впечатление от Android Jetpack Compose
После того, как на Google IO 2019 я увидел доклад про Android Jetpack Compose, захотелось сразу же его попробовать. Тем более, что подход, реализованный в нём, очень напомнил Flutter, которым я интересовался ранее.
Сама библиотека Compose находится в пре-альфа стадии, поэтому доступно не так много документации и статей про нее. Далее я буду полагаться на несколько ресурсов, которые мне удалось найти, плюс открытые исходники библиотеки.
Вот эти ресурсы:
Что такое Android Jetpack Compose?
Раньше весь UI в Android был основан на классе View. Так повелось с первых дней Android. И в связи с этим накопилось множество легаси и архитектурных недостатков, которые могли бы быть улучшены. Но сделать это достаточно сложно, не сломав весь код, написанный на их основе.
За последние годы появилось множество новых концептов в мире клиентских приложений (включая веяния Frontend-а), поэтому команда Google пошла радикальным путём и переписала весь UI-уровень в Android с нуля. Так и появилась библиотека Android Jetpack Compose, включающая в себя концептуальные приёмы из React, Litho, Vue, Flutter и многих других.
Давайте пройдемся по некоторым особенностям существующего UI и сравним его с Compose.
1. Независимость от релизов Android
Существующий UI тесно связан с платформой. Когда появились первые компоненты Material Design, они работали только с Android 5 (API21) и выше. Для работы на старых версиях системы необходимо использовать Support Library.
Compose же входит в состав Jetpack, что делает его независимым от версий системы и возможным для использования даже в старых версиях Android (как минимум с API21).
2. Весь API на Kotlin
Раньше приходилось иметь дело с разными файлами, чтобы сделать UI. Мы описывали разметку в xml, а затем использовали Java/Kotlin код, чтобы заставить ее работать. Затем мы снова возвращались в другие xml-файлы для того чтобы задать темы, анимацию, навигацию,… И даже пытались писать код в xml (Data Binding).
Использование Kotlin позволяет писать UI в декларативном стиле прямо в коде вместо xml.
3. Composable = Композитный: использование композиции вместо наследования
Создание кастомных элементов UI может быть довольно громоздким. Нам необходимо унаследоваться от View или его потомка и позаботиться о многих важных свойствах перед тем, как он правильно заведется. Например, класс TextView содержит около 30 тысяч строк Java-кода. Это связано с тем, что он содержит множество лишней логики внутри себя, которую наследуют элементы-потомки.
Compose подошел с другой стороны, заменяя наследование композицией.
Padding как нельзя лучше подойдет для иллюстрации того, о чем речь:
В существующем UI для того, чтобы отрисовать TextView c отступами в 30dp :
нам нужно написать следующий код:
Это означает, что где-то внутри TextView.java или его суперклассов содержится логика, которая знает, как посчитать и отрисовать отступы.
Давайте посмотрим, как можно сделать то же самое в Compose:
Изменения
TextView стал просто Text() . Свойство android:padding превратилось в Padding , который оборачивает Text .
Преимущества
Таким образом, Text отвечает только за отрисовку непосредственно текста. Он не знает про то, как считать отступы. С другой стороны, Padding отвечает только за отступы и ничего больше. Он может быть использован вокруг любого другого элемента.
4. Однонаправленный поток данных
Однонаправленный поток данных является важным концептом, если мы говорим, например, про управление состоянием CheckBox в существующей системе UI. Когда пользователь тапает на CheckBox , его состояние становится checked = true : класс обновляет состояние View и вызывает callback из кода, который следит за изменением состояния.
Затем в самом коде, например, во ViewModel , вам нужно обновить соответствующую переменную state . Теперь у вас есть две копии нажатого состояния, которые могут создать проблемы. Например, изменение значения переменной state внутри ViewModel вызовет обновление CheckBox , что может закончиться бесконечным циклом. Чтобы избежать этого, нам придется придумывать какой-то костыль.
Использование Compose поможет решить эти проблемы, так как в его основе заложен принцип однонаправленности. Изменение состояния будет обрабатываться внутри фреймворка: мы просто отдаем модель данных внутрь. Кроме того, компонент в Compose теперь не меняет свое состояние самостоятельно. Вместо этого он только вызывает callback, и теперь это задача приложения изменить UI.
5. Улучшение отладки
Так как теперь весь UI написан на Kotlin, теперь можно дебажить UI. Я не попробовал это сам, но в подкасте говорили, что в Compose работают дебаггер и точки остановки.
Хватит слов, покажите код
Я знаю, хочется поскорее увидеть, как выглядит UI в коде (спойлер: очень похоже на Flutter, если вы пробовали писать на нем).
Мы начнем с создания нескольких простых View , затем сравним как они выглядят в существующем UI и в Compose.
1. FrameLayout vs Wrap + Padding + Background
Переиспользуем наш пример выше и попробуем сделать этот TextView с отступами в 30dp и бирюзовым фоном:
Теперь посмотрим на код, который делает то же самое в Compose:
Здесь появляется несколько новых вещей. Так как Text знает только про рендеринг текста, он не заботится об отступах и фоне. Поэтому, чтобы добавить их, нам нужно использовать три отдельные функции:
- DrawRectangle отрисовывает фон
- Padding отрисовывает отступы
- Wrap — функция, которая накладывает параметры друг на друга, как FrameLayout .
Легко. Но немного отличается от существующей UI-системы, к который мы все привыкли.
2. Вертикальный LinearLayout vs Column
Теперь попробуем сделать что-то эквивалентное нашему старому доброму LinearLayout .
Чтобы поместить два элемента один под другим, как на картинке ниже, мы можем использовать Column :
Код будет выглядеть так:
Вложенные в Column элемент будут расположены вертикально друг под другом.
2a. Отступы
Вероятно, вы заметили, что текст и кнопка расположены слишком близко к краю. Поэтому добавим Padding .
2b. Интервалы
Мы можем также добавить немного отступов между Text и Button :
Как выглядит наш экран теперь:
2c. Горизонтальный LinearLayout vs Row
Поместим вторую кнопку рядом с первой:
Внутри Row две кнопки будут расположены горизонтально. WidthSpacer добавляет расстояние между ними.
2d. Gravity vs Alignment
Выровняем наши элементы по центру, как это делает gravity в текущем UI. Чтобы показать diff, я закомментирую старые строки и заменю их новыми:
У нас получится:
С crossAxisAlignment = CrossAxisAlignment.Center вложенные элементы будут выравнены по горизонтали по центру. Мы должны также выставить Row параметр mainAxisSize = FlexSize.Min , похожий по поведению на layout_width = wrap_content , чтобы он не растягивался по всему экрану из-за дефолтного mainAxisSize = FlexSize.Max , который ведет себя как layout_width = match_parent .
2d. Замечание
Из того, что мы видели в примерах выше, можно заметить, что все элементы строятся композитно из отдельных функций: padding — отдельная функция, spacer — отдельная функция, вместо того, чтобы быть свойствами внутри Text , Button или Column .
Более сложные элементы, такие как RecyclerView или ConstraintLayout находятся в разработке: поэтому я не смог найти пример с ними в демонстрационных исходниках.
3.Стили и темы
Вы, вероятно, заметили, что кнопки выше по умолчанию фиолетовые. Это происходит потому, что они используют стили по умолчанию. Рассмотрим, как работают стили в Compose.
В примерах выше FormDemo помечена аннотацией @Composable . Теперь я покажу, как этот элемент используется в Activity :
Вместо функции setContentView() мы используем setContent() — функция-расширение из библиотеки Compose.kt .
CraneWrapper содержит дерево Compose и предоставляет доступ к Context , Density , FocusManager и TextInputService .
MaterialTheme позволяет кастомизировать тему для элементов.
Например, я могу изменить основной цвет темы (primary color) на каштановый следующим образом:
Теперь наш экран будет выглядеть так:
Другие цвета и шрифты, которы можно поменять: MaterialTheme.kt#57
Rally Activity содержит хороший пример, как можно кастомизировать тему: source code to RallyTheme.kt
Что посмотреть/почитать
Если вы хотите большего, вы можете собрать проект-образец по инструкции тут.
Как пишут пользователи Windows, сейчас не существует официального способа запустить Compose, но есть неофициальный гайд из kotlinlang Slack.
Вопросы про Compose можно задать разработчикам в канале #compose kotlinlang Slack.
Оставляйте другие ссылки в комментариях — самые полезные добавлю сюда.
Выводы
Разработка этой библиотеки идет полным ходом, поэтому любые интерфейсы, показанные здесь могут быть изменены. Остается еще множество вещей, про которые можно узнать в исходном коде, как например @Model и Unidirectional data flow (однонаправленный поток данных). Возможно, это тема для будущих статей.
Источник