Что происходит при переходе с одного фрагмента на другой и возврате обратно в Android
Что проверяют
Знаете ли вы, что фрагмент и его верстка живут разными жизнями. Отсюда почти все баги навигации: задвоенные подписки, потерянное состояние, обращение к уже разрушенным view. Вопрос простой на словах, но ответ сразу показывает, чинили ли вы такое.
Как отвечать
Разберите два перехода отдельно.
replace с добавлением в back stack: у текущего фрагмента вызываются onPause, onStop, onDestroyView. Сам фрагмент остаётся жив, onDestroy и onDetach не вызываются, поля класса сохраняются. Уничтожается только верстка, её состояние сохраняется механизмом view state по идентификаторам.
Возврат кнопкой назад или popBackStack: поднимается тот же экземпляр, но верстка создаётся заново, дальше идут onCreateView, onViewCreated, onStart, onResume. Поэтому ссылки на view обнуляют в onDestroyView, а подписки вешают на viewLifecycleOwner.
add ведёт себя иначе: нижний фрагмент не останавливается и продолжает получать события, хотя визуально перекрыт.
Дальше про состояние: экранные данные держите в ViewModel, она переживает пересоздание верстки и поворот, а то, что должно пережить смерть процесса, кладите в SavedStateHandle. Уместно упомянуть, что onActivityCreated объявлен устаревшим, его работу делят onViewCreated и onStart.
Где спотыкаются
Подписываются на данные, передавая this вместо viewLifecycleOwner. После возврата из back stack наблюдатель добавляется второй раз, обработчик срабатывает дважды, экран мигает или уходит дубль запроса.
Второе: держат байндинг в поле и не чистят его в onDestroyView. Ссылка переживает верстку, отложенный колбэк дёргает мёртвые view, и приложение падает или тихо рисует в дерево, которого уже нет на экране.
Третье: ждут, что состояние вернёт onSaveInstanceState фрагмента. При обычной навигации по back stack он не вызывается, срабатывает только сохранение состояния хоста. Вторая половина той же ошибки: у view нет идентификатора, и тогда введённый текст не восстанавливается вообще.