В чем разница между методами replace и add при работе с фрагментами
Что проверяют
Знаете ли вы, что происходит с жизненным циклом и View старого фрагмента в каждом случае, а не только «один заменяет, другой добавляет». Следом почти всегда спрашивают, как ведёт себя кнопка «Назад» и при чём здесь addToBackStack.
Как отвечать
add: кладёт новый фрагмент в контейнер поверх существующих. Старый остаётся в состоянииRESUMED, его View живёт и рисуется под новым, колбэки жизненного цикла у него не вызываются;replace: удаляет все фрагменты, добавленные в этот контейнер, и добавляет новый. По сути этоremoveдля каждого плюсadd;addToBackStack: отдельный механизм, работает с обоими методами. Без него старый фрагмент послеreplaceуничтожается полностью, вплоть доonDestroy, и вернуться к нему нельзя. С ним у старого вызывается толькоonDestroyView: экземпляр и его состояние сохраняются, а View при возврате создаётся заново черезonCreateView.
Отсюда практические выводы. replace подходит для обычной навигации между экранами, так работает и Navigation Component. add нужен, когда предыдущий экран должен остаться живым: тяжёлый список, который дорого пересоздавать, или оверлей. Для вкладок часто делают add один раз и дальше show/hide, дополняя это setMaxLifecycle, чтобы скрытые фрагменты не оставались в RESUMED.
Стоит упомянуть setReorderingAllowed(true): его рекомендуют включать для всех транзакций, он позволяет системе оптимизировать промежуточные операции и нужен для корректных анимаций переходов.
Где спотыкаются
Говорят, что add сам кладёт фрагмент в back stack. Нет, стек возврата заполняет только addToBackStack, независимо от метода.
Не учитывают, что после add нижний фрагмент продолжает получать нажатия. Если у верхнего прозрачный фон и корневой View не кликабелен, пользователь попадает по кнопкам нижнего экрана.
Держат ссылки на View в полях фрагмента после replace с back stack. View уже уничтожена, а фрагмент жив, получается утечка памяти. Ссылку на биндинг обнуляют в onDestroyView.