Главная » Хабрахабр » Navigation Architecture Component. Практический взгляд

Navigation Architecture Component. Практический взгляд

На недавнем Google IO 2018 в числе прочего было представлено решение, помогающее в реализации навигации в приложениях.

Это действительно интересно и ново для Android-разработчиков. Сразу бросилось в глаза то, что граф навигации можно просмотреть в UI редакторе, чем-то напоминающем сториборды из iOS-разработки.

Поэтому, как только появилось свободное время, я создал тестовый проект и стал разбираться. А так как я еще являюсь создателем довольно популярной библиотеки Cicerone, которая позволяет работать с навигацией на андроиде, то я получил множество вопросов на тему того, пора ли мигрировать на решение Google, и чем оно лучше/хуже.

Постановка задачи

Я не буду повторять вводные слова про новый архитектурный компонент Navigation Architecture Component, про настройку проекта и основные сущности в библиотеке (это уже сделал Саша Блинов), а пойду с практической стороны и попробую библиотеку в бою, изучу возможности и применимость к реальным кейсам.

Я накидал на листе бумаги граф экранов с переходами, которые хотел бы реализовать.

Хочу отметить, что навигация в данном случае рассматривается в пределах одного Activity, такой подход я практикую уже не на первом проекте, и он себя отлично показал (обсуждение этого подхода — не цель данной статьи).

Таким графом я постарался описать наиболее частые кейсы, которые встречаются в приложениях:

  1. A — стартовый экран. С него приложение начинает работу при запуске из меню.
  2. Есть несколько прямолинейных переходов: A->B, B->C, C->E, B->F
  3. Из A можно запустить отдельный флоу a-b
  4. Из F можно переходить на новые экраны F, выстраивая цепочку одинаковых F-F-F-...
  5. Из F тоже можно запустить флоу a-b
  6. Флоу a-b — это последовательность двух экранов, где с экрана b можно выйти из флоу на тот экран, с которого был произведен запуск флоу
  7. Из экрана C можно перейти на D, заменив экран, то есть, из цепочки A-B-C, перейти к A-B-D
  8. Из экрана E можно перейти на D, заменив два экрана, то есть из цепочки A-B-C-E, перейти к A-B-D
  9. С экрана E можно вернуться на экран B
  10. С экрана D можно вернуться на экран A

Реализация

После переноса такого графа в код тестового Android-приложения получилось красиво:

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

1. Стартовый экран устанавливается свойством

app:startDestination="@id/AFragment"

работает как и предполагалось, приложение запускается именно с него.

2. Прямолинейные переходы выглядят и работают тоже правильно

и 5. Вложенная навигация легко описывается и запускается как простой экран. 3. И тут первое интересное свойство: я ожидал, что вложенная навигация будет как-то связана с вложенными фрагментами, но оказалось, что это просто удобное представление в UI-графе, а на практике все работает на одном общем стеке фрагментов.

И тут можно воспользоваться специальным свойством перехода 4. Запуск цепочки одинаковых экранов делается без проблем.

app:launchSingleTop="true"

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

Тут скрыт первый баг: если открыть несколько раз экран F с параметром app:launchSingleTop="true", а потом нажать кнопку «назад», то будет произведен возврат на экран B, но экран F останется «висеть» сверху.

Могу предположить, что это происходит из-за того, что фрагмент менеджер внутри сохранил транзакцию с B на первый инстанс F, а мы его уже сменили на новый, который не будет ему известен.

6. Отдельный флоу из двух экранов работает прекрасно, но проблема в том, что нельзя явно описать поведение, что из второго экрана этого флоу надо вернуться на запустивший экран!

Все стрелки — это создание нового экрана и переход на него! Здесь мы подошли к важному свойству UI-графа: на графе нельзя описать переходы назад!

Самое простое решение для нашего случая — это вызвать два раза подряд popBackStack(), новая навигация здесь ничем не помогает. То есть чтобы сделать переход «назад» по стеку, надо обязательно работать с кодом. А для более сложных флоу надо будет выдумывать другие решения.

Navigation.findNavController(view).apply { popBackStack() popBackStack()
}

7. Заменить экран C на D можно, указав в свойствах перехода

app:popUpTo="@+id/BFragment"

Тоже не самое лучшее место в новой библиотеке. как видите, здесь мы жестко завязали логику перехода между C и D на предыдущий экран B.

8. Аналогично и с переходом с E на D — добавляем app:popUpTo="@+id/BFragment", и работает как мы хотели, но с привязкой к экрану B

Поэтому возврат с экрана E на экран B можно сделать только из кода, вызвав popBackStack(R.id. 9. Как я объяснил выше, возвратов на UI-графе нет. Но если вам не обязательно именно вернуться на экран B, а допустимо создать новый экран B, то делается это переходом на UI-графе с установкой двух параметров: BFragment) до необходимого экрана.

app:popUpTo="@+id/BFragment"
app:popUpToInclusive="true"

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

Надо делать его из кода. 10. Возврат на экран A, как вы уже поняли, невозможно описать на графе. Для этого создаем переход из D на A и добавляем специальное свойство Но по примеру предыдущего кейса, можно скинуть все экраны и создать новый экран A в корне.

app:clearTask="true"

Теперь при вызове такого перехода, сначала будет очищен весь стек фрагментов.

Остался один неизученный параметр из UI редактора навигации:

app:launchDocument="true"

Я так и не понял его предназначение, а скорее всего он просто не работает так, как заявлено в документации:

If the same document is launched multiple times it will not create a new task, it will bring the existing document task to the front. Launch a navigation target as a document if you want it to appear as its own entry in the system Overview screen.

If the user reached the document task from the system Overview screen they will be taken to their home screen. If the user presses the system Back key from a new document task they will land on their previous task.

Выводы

Всем стрелкам-переходам выдается уникальный ID и набор параметров, по которым совершается транзакция фрагментов на старом «добром» фрагмент менеджере. Из моих экспериментов и небольшого изучения исходников можно сделать вывод: новая навигация — это просто UI обертка работы с фрагмент менеджером (для случая приложения в одном Activity). Библиотека еще в глубокой альфе, но уже сейчас заметны баги, которые прямо указывают на известные проблемы фрагмент менеджера.

Если нам надо как-то повлиять на стек фрагментов и сделать возврат дальше предыдущего экрана, то придется работать по старинке с фрагмент менеджером.

IllegalStateException: Can not perform this action after onSaveInstanceState Если вызвать навигацию при свернутом приложении, приложение упадет с классическим
java.lang.

Чтобы совершить навигацию нужен доступ к view для поиска navController
Navigation.findNavController(view)

Зато можно объединить! Именно эти проблемы и решает библиотека Cicerone, поэтому сравнивать их мало смысла. Потому что у Google-навигации есть и полезные стороны:

  • Визуальный граф. Для небольших проектов может быть очень полезной подсказкой.
  • Описание входных параметров экрана и проверка без запуска приложения прямо в студии.
    Для этого надо подключить дополнительный Gradle плагин и описывать обязательные параметры в xml свойствах экрана.

    <fragment
    android:id="@+id/confirmationFragment"
    android:name="com.example.cashdog.cashdog.ConfirmationFragment"
    android:label="fragment_confirmation"
    tools:layout="@layout/fragment_confirmation">
    <argument android:name="amount" android:defaultValue=”0” app:type="integer" />

  • Привязка Deep Link к запуску любого экрана из графа.
    Мне не удалось проверить эту фичу, но есть подозрение, что она так же сделана в лоб, поэтому просто будет другой корневой экран, а не полная цепочка экранов, как хотелось бы. Но это не точно.
  • Возможность создавать переиспользуемые части графа — “Nested Graph”. То есть выносить некоторую часть графа во вложенный граф, который можно запускать из нескольких мест.

Тестовый проект

Все эксперименты можно посмотреть и потрогать на Github


Оставить комментарий

Ваш email нигде не будет показан
Обязательные для заполнения поля помечены *

*

x

Ещё Hi-Tech Интересное!

Профессиональные навыки, востребованные среди UX-специалистов (срез 2018)

С 29 августа по 07 сентября 2018 сообщество UX SPb (независимое сообщество UX-специалистов Санкт-Петербурга) проводило опрос, направленный на изучение профессиональных навыков специалистов по пользовательским интерфейсам. Сообщество обещало опубликовать результаты. Обещание исполнено 🙂 В исследовании приняли участие 109 респондентов. Опрос проводился ...

От антикварного радио до DIY-колонок: 12 каналов на YouTube про устройство акустики

Сегодня мы подготовили подборку YouTube-каналов, авторы которых рассказывают об устройстве винтажного и современного аудиооборудования: от настройки виниловых проигрывателей до сборки акустических систем. Всех, кому это интересно, приглашаем к просмотру под кат. Фото Nathan Duprey / CC Канал посвящен сборке акустических ...