Что делает метод replace в транзакциях фрагментов Android
Что проверяют
Понимаете ли вы разницу с добавлением и знаете ли, что происходит с представлением при возврате назад.
Как отвечать
Метод удаляет все фрагменты, ранее добавленные в указанный контейнер, и помещает туда новый. Это его отличие от добавления, которое кладёт фрагмент поверх, оставляя предыдущий живым и видимым снизу.
supportFragmentManager.beginTransaction()
.replace(R.id.container, DetailFragment())
.addToBackStack(null)
.commit()Ключевая тонкость, ради которой вопрос и задают: что происходит со старым фрагментом.
- без добавления в стек возврата он уничтожается полностью;
- с добавлением в стек возврата сам объект фрагмента остаётся жив, а уничтожается только его представление. При возврате назад представление создаётся заново, а фрагмент тот же самый.
Из этого вытекают два практических правила, нарушение которых даёт классические дефекты:
- подписки привязываются к владельцу жизненного цикла представления, а не к самому фрагменту. Иначе при возврате назад подписка создастся второй раз, и обработчик выполнится дважды;
- ссылка на разметку обнуляется при уничтожении представления, иначе фрагмент удержит уничтоженное дерево, и это утечка памяти.
Про саму транзакцию: она асинхронна, изменения применяются не мгновенно, а при следующем проходе главного цикла. Есть синхронный вариант, а также вариант, разрешающий потерю состояния, который выглядит спасением от падения после сохранения состояния, но на деле маскирует проблему и приводит к пропаже экрана после возврата из фона.
Полезно упомянуть, что вручную транзакции сегодня пишут редко: компонент навигации делает то же самое, а поверх даёт граф переходов, аргументы с проверкой типов и обработку возврата.
Где спотыкаются
Подписываются с владельцем-фрагментом вместо владельца-представления.
Не обнуляют ссылку на разметку.
Используют вариант с потерей состояния, чтобы убрать падение.
Ждут, что транзакция применится сразу после вызова.