Русский
Русский
English
Статистика
Реклама

Получаем результат правильно(Часть2). FragmentResultAPI

Мы продолжаем рассказ о новинках библиотеки Jetpack, призванных упростить обмен данными между компонентами Android приложения. Первая часть была посвящена передаче данных из Activity и новому Api Activity Result.

На этот раз посмотрим, какое решение Google предлагает для Fragment. Ввиду популярности паттерна Single Activity работа с фрагментами представляет большой практический интерес для многих Android-разработчиков.

Как передать данные между двумя фрагментами? - частый вопрос на собеседованиях. Ответить на него можно по-разному: создание общей ViewModel, имплементация интерфейса в Activity, использование targetFragment и другие способы.

С появлением Fragment Result Api в этот список добавился простой способ передачи небольшого объема информации из одного фрагмента в другой. Например, возвращение результата какого-либо пользовательского сценария. Мы разберем, как применять новый Api на практике, но сначала немного теории.

Теория

Начиная с версии 1.3.0-alpha04, FragmentManager реализует интерфейс FragmentResultOwner. Это означает, что FragmentManger является диспетчером для результатов, которые отправляют фрагменты. Благодаря этому фрагменты могут обмениваться информацией, не имея прямых ссылок друг на друга.

Таким образом, всё взаимодействие происходит через FragmentManager:

  • Если фрагмент ожидает получить некоторые данные от другого фрагмента, он должен зарегистрировать слушатель во FragmentManger с помощью метода setFragmentResultListener().

  • Если фрагменту необходимо вернуть результат другому фрагменту, он передает FragmentManger объект Bundle, содержащий информацию. Для этого вызывается метод setFragmentResult().

  • Чтобы FragmentManger знал, как соотнести Bundle с нужным слушателем, необходимо указывать строковый ключ при регистрации слушателя и при передаче результата.

Упрощенно данную схему можно представить так:

FragmentB передает данные в FragmentA . FragmentManager выполняет роль диспетчераFragmentB передает данные в FragmentA . FragmentManager выполняет роль диспетчера

Достоинством Fragment Result Api является lifecycle-безопасность - результат передается во фрагмент, только когда тот достиг состояния STARTED, но еще не находится в состоянии DESTROYED.

Под капотом FragmentManger хранит все зарегистрированные слушатели и все отправленные результаты в потокобезопасных реализациях Map:

  • Map<String, Bundle> для результатов, отправленных фрагментами

  • Map<String, LifecycleAwareResultListener> для зарегистрированных слушателей

Когда фрагмент регистрирует FragmentResultListener, FragmentManager добавляет его в Map, а при уничтожении фрагмента, слушатель удаляется из Map. Для того, чтобы учитывать жизненный цикл фрагмента, FragmentResultListener оборачивается в LifecycleAwareResultListener.

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

А теперь практика.

Практика

В качестве примера возьмем следующий кейс: ProductsFragment содержит список товаров, которые можно сортировать по различным критериям, а SortFragment позволяет указать нужную сортировку. Информация о выбранной сортировке будет передаваться с помощью Fragment Result Api.

Так выглядит итоговая реализация, которую можно найти по ссылке нижеТак выглядит итоговая реализация, которую можно найти по ссылке ниже

В коде все выглядит довольно просто. Чтобы наладить передачу результата, необходимо выполнить всего два шага.

Шаг 1

В ProductsFragment, который ожидает получить результат, мы должны зарегистрировать слушатель с помощью FragmentManager. Для этого воспользуемся экстеншен-функцией setFragmentResultListener из fragment-ktx, которая принимает строковый ключ и слушатель, обрабатывающий результат.

Регистрацию слушателя можно произвести в колбеке onCreate():

override fun onCreate(savedInstanceState: Bundle?) {   super.onCreate(savedInstanceState)   setFragmentResultListener("request_key") { key, bundle ->        val selectedSort = bundle.getParcelable<Sort>("extra_key")        // применение полученной сортировки   }}

Шаг 2

Когда SortFragment будет готов отправить результат, вызывается метод setFragmentResult, в который передается тот же строковый ключ и заполненный объект Bundle.

applyButton.setOnClickListener {   setFragmentResult(      "request_key",       bundleOf("extra_key" to getSelectedSort())   )}

Вот и всё, что требуется для передачи результата с помощью Fragment Result Api.

Важно

Хотя Api довольно прост, стоит разобрать некоторые нюансы его работы, связанные с правильным выбором FragmentManager и жизненным цикломфрагментов.

Выбор FragmentManager

FragmentManager выполняет основную работу в передаче результата от одного фрагмента к другому. Но каждому фрагменту доступен выбор из нескольких вариантов: parentFragmentManager, childFragmentManager и FragmentManager у активити-хоста. Разберемся, в каких случаях стоит выбирать тот или иной FragmentManager.

Сначала представим так называемую master-detail конфигурацию. Активити содержит два фрагмента, FragmentA и FragmentB, между которыми требуется передать результат.

Активити является хостом для FragmentA и FragmentBАктивити является хостом для FragmentA и FragmentB

В таком случае передавать результат между фрагментами может FragmentManager активити-хоста, т.к. доступ к нему имеют оба фрагмента. Получить данный FragmentManager можно путем вызова requireActivity().supportFragmentManager либо parentFragmentManager.

Следующая ситуация характерна, например, для открытия DialogFragment или в случае, если FragmentA размещает внутри себя FragmentC.

FragmentA является хостом для FragmentСFragmentA является хостом для FragmentС

При таком сценарии, передать результат из FragmentС в FragmentA можно двумя способами:

  • Через FragmentManager активити с помощью requireActivity().supportFragmentManager

  • Через дочерний FragmentManager у FragmentA. Чтобы получить на него ссылку, FragmentA должен обращаться к childFragmentManager, а FragmentС к parentFragmentManager.

Особенности Lifeсycle

Как уже сказано, Fragment Result Api обеспечивает lifecycle-безопасность - результат доставляется, только если фрагмент находится на экране. Рассмотрим несколько примеров.

Представим стандартный случай - фрагмент подписывается в колбеке onCreate, затем переходит в состояние STARTED, и как только другой фрагмент передает во FragmentManager результат, фрагмент-подписчик его получает.

Фрагмент получит лишь bundle3, так как он был отправлен последнимФрагмент получит лишь bundle3, так как он был отправлен последним

Если еще до перехода фрагмента в состояние STARTED, во FragmentManager было передано несколько результатов, то фрагмент получит лишь последний из них (так как FragmentManager хранит результаты в Map<String, Bundle>, то каждый последующий перезаписывает предыдущий).

Автоматическая отписка фрагментов происходит при достижении состояния DESTROYEDАвтоматическая отписка фрагментов происходит при достижении состояния DESTROYED

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

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

В том случае, когда фрагмент не закрыт окончательно, а лишь находится в бэкстеке (в таком случае он в состоянии CREATED), то результат будет доставлен, как только пользователь вернется к этому фрагменту.

Сценарий при нахождении фрагмента в бэкстеке в момент передачи результатаСценарий при нахождении фрагмента в бэкстеке в момент передачи результата

Все рассмотренные ситуации объединяет то, что фрагмент подписывался по уникальному строковому ключу. Но что если сразу несколько подписчиков будут использовать один и тот же ключ? Напомним, что FragmentManager сохраняет информацию о подписках в Map<String, LifecycleAwareListener>, следовательно не может содержать несколько записей с одним и тем же ключом. Именно поэтому результат будет доставлен в тот фрагмент, который зарегистрировал слушатель последним.

Результат получает только последний подписчикРезультат получает только последний подписчик

Заключение

Подводя итог, отметим достоинства нового способа передачи результата между фрагментами:

  • Fragment Result Api является стабильным, можно не бояться использовать его в продакшене. Тем, кто использует targetFrament особенно стоит присмотреться, ведь targetFrament стал Deprecated.

  • Api прост в использовании и не требует написания большого количества кода

  • Учитывает жизненный цикл фрагментов - при получении результата, можно сразу работать со view фрагмента

  • Позволяет пережить изменение конфигурации и даже смерть процесса (FragmentManager умеет сохранять данные о переданных результатах в Parcelable)

Но присутствуют и недостатки:

  • необходимо следить за уникальностью строковых ключей, а также выбирать правильное место для их хранения

  • так как результат передается в Bundle, отсутствует его типизация. При неаккуратном обращении, можно получить ClassCastException.

В целом, Fragment Result Api оставляет положительное впечатление, и точно стоит того, чтобы его опробовать, а наглядный пример можно найти по ссылке.

Источник: habr.com
К списку статей
Опубликовано: 25.05.2021 20:20:29
0

Сейчас читают

Комментариев (0)
Имя
Электронная почта

Блог компании e-legion

Разработка мобильных приложений

Разработка под android

Android

Fragment

Google

Mobile development

Kotlin

Категории

Последние комментарии

  • Имя: Макс
    24.08.2022 | 11:28
    Я разраб в IT компании, работаю на арбитражную команду. Мы работаем с приламы и сайтами, при работе замечаются постоянные баны и лаги. Пацаны посоветовали сервис по анализу исходного кода,https://app Подробнее..
  • Имя: 9055410337
    20.08.2022 | 17:41
    поможем пишите в телеграм Подробнее..
  • Имя: sabbat
    17.08.2022 | 20:42
    Охренеть.. это просто шикарная статья, феноменально круто. Большое спасибо за разбор! Надеюсь как-нибудь с тобой связаться для обсуждений чего-либо) Подробнее..
  • Имя: Мария
    09.08.2022 | 14:44
    Добрый день. Если обладаете такой информацией, то подскажите, пожалуйста, где можно найти много-много материала по Yggdrasil и его уязвимостях для написания диплома? Благодарю. Подробнее..
© 2006-2024, personeltest.ru