← Назад к вопросам

Будет ли общая ViewModel при использовании данных одного фрагмента в другом фрагменте при переходах?

2.0 Middle🔥 122 комментариев
#UI и вёрстка#Жизненный цикл и навигация

Комментарии (2)

🐱
deepseek-v3.2PrepBro AI5 апр. 2026 г.(ред.)

Ответ сгенерирован нейросетью и может содержать ошибки

Краткий ответ

Да, будет. Использование общей (разделяемой) ViewModel — это стандартный и рекомендованный подход для передачи данных между фрагментами, особенно когда они находятся в пределах одной активности, навигационного графа или области видимости (scope). Это позволяет эффективно избежать прямого взаимодействия фрагментов и утечек памяти.

Подробное объяснение

Ключевая идея заключается в том, что оба фрагмента должны получать одну и ту же инстанцию ViewModel, обращаясь к ней через одного и того же «владельца» (owner). По умолчанию это ViewModelProvider, связанный с родительской Activity.

Как это работает

  1. Область видимости ViewModel (Scope): Вы создаете ViewModel не в пределах одного фрагмента, а в пределах компонента с более широким жизненным циклом.
  2. Общий владелец: Когда оба фрагмента запрашивают ViewModel, указывая одну и ту же Activity или область видимости Navigation Graph в качестве владельца, они получают один и тот же экземпляр.

Ключевые реализации

1. Использование родительской Activity (классический подход)

Это самый распространенный сценарий, когда фрагменты находятся в рамках одной Activity.

// В первом фрагменте (FragmentA) или втором (FragmentB) создаем/получаем ViewModel
class FragmentA : Fragment() {
    private lateinit var sharedViewModel: SharedViewModel

    override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
        super.onViewCreated(view, savedInstanceState)

        // Activity выступает в роли владельца (owner)
        sharedViewModel = ViewModelProvider(requireActivity()).get(SharedViewModel::class.java)

        // Обновляем данные, которые будут нужны другому фрагменту
        sharedViewModel.selectedItem.value = "Новые данные"
    }
}

class FragmentB : Fragment() {
    private lateinit var sharedViewModel: SharedViewModel

    override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
        super.onViewCreated(view, savedInstanceState)

        // Получаем ТУ ЖЕ САМУЮ ViewModel через requireActivity()
        sharedViewModel = ViewModelProvider(requireActivity()).get(SharedViewModel::class.java)

        // Подписываемся на данные из FragmentA
        sharedViewModel.selectedItem.observe(viewLifecycleOwner) { item ->
            // Обновляем UI на основе полученных данных
            textView.text = item
        }
    }
}

2. Использование Navigation Component с графом навигации (современный подход)

Jetpack Navigation предоставляет более строгий способ, привязывая ViewModel к NavGraph. Это особенно полезно, если оба фрагмента являются частью одного логического потока (например, master/detail) и ViewModel должна очищаться при выходе из этого графа.

// В обоих фрагментах
class FragmentA : Fragment() {
    private val sharedViewModel: SharedViewModel by navGraphViewModels(R.id.my_nav_graph)

    // ... использование sharedViewModel
}

class FragmentB : Fragment() {
    // Получим тот же экземпляр, что и в FragmentA, так как ID графа одинаковый
    private val sharedViewModel: SharedViewModel by navGraphViewModels(R.id.my_nav_graph)

    // ... использование sharedViewModel
}

В этом случае R.id.my_nav_graph — это ID вашего <navigation>-графа в XML, содержащего оба фрагмента.

Преимущества такого подхода

  • Изоляция логики данных от UI: Фрагменты управляют только своим представлением и не знают о способе передачи данных.
  • Сохранение состояния при поворотах экрана: ViewModel переживает изменения конфигурации, данные не теряются.
  • Избегание утечек памяти: Корректное использование viewLifecycleOwner для наблюдения за LiveData гарантирует, что подписки будут очищены при разрушении View фрагмента.
  • Тестируемость: ViewModel легко протестировать отдельно от фрагментов.
  • Согласованность данных: Оба фрагмента работают с единым источником истины (single source of truth).

Важные нюансы

  • Не используйте ViewModel как Event Bus. ViewModel предназначена для хранения состояния UI, а не для передачи одноразовых событий (например, «показать Toast»). Для событий используйте паттерны типа SingleLiveEvent или SharedFlow/StateFlow из Kotlin Coroutines с обработкой повторов.
  • Используйте правильный LifecycleOwner. При наблюдении за LiveData из ViewModel внутри onViewCreated() всегда передавайте viewLifecycleOwner, а не this (lifecycle фрагмента). Это предотвратит утечки и обновление уничтоженного View.
  • Очистка: Если данные должны жить только пока активна Activity, используйте ее как scope. Если данные нужны только в рамках определенного навигационного потока — используйте navGraphViewModels.

Альтернатива (менее предпочтительная)

Иногда можно встретить передачу данных через аргументы (Arguments) с помощью Bundle при использовании FragmentManager. Однако этот способ подходит только для простых, сериализуемых данных, передаваемых однократно при создании фрагмента. Для реактивного обмена данными в реальном времени или сложных объектов он не годится, и здесь общая ViewModel — безальтернативное решение.

Вывод: При необходимости постоянного или реактивного обмена данными между фрагментами использование общей ViewModel, привязанной к Activity или навигационному графу, — это идиоматичный, надежный и рекомендуемый Google паттерн архитектуры Android.

Будет ли общая ViewModel при использовании данных одного фрагмента в другом фрагменте при переходах? | PrepBro