Pull to refresh
EPAM
Компания для карьерного и профессионального роста

Compose. Jetpack Compose

Reading time 6 min
Views 27K
image

Благодаря стремительному развитию мобильной индустрии каждые несколько лет мы наблюдаем появления новых технических решений, призванных усложнить упростить жизнь разработчикам. Некоторые из них, не сыскав популярности у пользователей, остаются лишь частью истории, другие – плотно укореняются в повседневной разработке, становясь в определенной области стандартом де-факто.

Пожалуй, главным трендом мобильной разработки за последние несколько лет стал декларативный 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-фреймворка вёрстка этих компонентов выглядит так:

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="match_parent"
    android:layout_height="wrap_content"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    android:orientation="vertical"
    android:padding="@dimen/padding_16dp">

    <com.google.android.material.textfield.TextInputLayout
        android:id="@+id/til_login"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        style="@style/Widget.MaterialComponents.TextInputLayout.OutlinedBox"
        android:hint="@string/sign_in_email"
        android:layout_marginBottom="@dimen/margin_8dp">

        <com.google.android.material.textfield.TextInputEditText
            android:id="@+id/et_login"
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            android:inputType="text"/>

    </com.google.android.material.textfield.TextInputLayout>

    <com.google.android.material.textfield.TextInputLayout
        android:id="@+id/til_password"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        style="@style/Widget.MaterialComponents.TextInputLayout.OutlinedBox"
        android:hint="@string/sign_in_password"
        android:layout_marginVertical="@dimen/margin_8dp">

        <com.google.android.material.textfield.TextInputEditText
            android:id="@+id/et_password"
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            android:inputType="textPassword"/>

    </com.google.android.material.textfield.TextInputLayout>

    <Button
        android:id="@+id/btn_confirm"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:text="@string/sign_in_submit"
        android:layout_marginTop="@dimen/margin_8dp"
        android:padding="@dimen/padding_8dp"
        android:background="@color/purple_700"/>

</LinearLayout>

В то же время, при использовании Jetpack Compose, решение будет выглядеть следующим образом:

@Preview
@Composable
fun LoginPage(){
    var loginValue by remember { mutableStateOf(TextFieldValue("")) }
    var passwordValue by remember { mutableStateOf(TextFieldValue("")) }

    Surface(color = Color.White) {
        Column(modifier = Modifier.padding(16.dp).fillMaxWidth()) {
            Surface(color = Color.White, modifier = Modifier.padding( vertical = dimensionResource(id = R.dimen.padding_8dp))) {
                OutlinedTextField(
                        value = loginValue,
                        onValueChange = { loginValue = it },
                        label = { Text(text = stringResource(id = R.string.sign_in_email)) },
                        placeholder = { Text(text = stringResource(id = R.string.sign_in_email)) },
                        modifier = Modifier.fillMaxWidth()
                )
            }
            Surface(color = Color.White, modifier = Modifier.padding( vertical = dimensionResource(id = R.dimen.padding_8dp))) {
                OutlinedTextField(
                        value = passwordValue,
                        onValueChange = { passwordValue = it },
                        label = { Text(text = stringResource(id = R.string.sign_in_password)) },
                        placeholder = { Text(text = stringResource(id = R.string.sign_in_password)) },
                        visualTransformation = PasswordVisualTransformation(),
                        modifier = Modifier.fillMaxWidth()
                )
            }
            Button(
                    onClick = {},
                    modifier = Modifier.padding( vertical = dimensionResource(id = R.dimen.padding_8dp)).fillMaxWidth(),
                    backgroundColor = colorResource(R.color.purple_700)) {
                Text(text = stringResource(id = R.string.sign_in_submit), modifier = Modifier.padding(8.dp))
            }
        }
    }
}

Ну и напоследок – сравнительный результат:

image

Недостатки


  • Alpha-версия: Безусловно, более чем за год разработки фреймворк значительно преобразился и стал гораздо стабильнее. Однако это всё ещё альфа, а поэтому за пределами Pet-проектов использовать его не рекомендуется.

Декларативный стиль


Отдельное внимание стоит уделить главной особенности 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-код. Перед описание компонентов были определены две переменные:

    var loginValue by remember { mutableStateOf(TextFieldValue("")) }
    var passwordValue by remember { mutableStateOf(TextFieldValue("")) }

Мы создаем два текстовых объекта, значения которых будем устанавливать полям ввода (логина и пароля) в качестве value. А благодаря связке remember { mutableStateOf(…) } любое изменение значений этих объектов (из других частей кода) уведомит об этом соответствующее поле ввода, которое перерисует только значение value, вместо полной рекомпозиции всего компонента.

Вывод


Какой же вывод можно сделать о Jetpack Compose? По моему мнению, у нового решения от Google имеется огромный потенциал. С момента анонса в 2019 году была проделана огромная работа, и не менее долгий путь до релиза у фреймворка ещё впереди. Однако теперь он публично доступен, и я считаю, что это прекрасная возможность познакомиться с ним поближе. Ну а за чем, по вашему мнению, будущее – пишите в комментарии, будет интересно узнать ваше мнение. Любите android!
Tags:
Hubs:
+5
Comments 12
Comments Comments 12

Articles

Information

Website
www.epam.com
Registered
Founded
1993
Employees
over 10,000 employees
Location
США
Representative
vesyolkinaolga