ViewModel для фрагмента вместо доступа к деятельности ViewModel?

Проблема довольно проста. Вопрос в том, что касается использования ViewModels, LiveData и других связанных с Lifecycle арочных подходов.
У меня есть активность с NavDrawer, который переключает фрагменты внутри.
А также у меня есть случай, когда на экране одновременно присутствуют два фрагмента - это будет основной болью. Один фрагмент имеет ViewPager с вложенным Fragments(не спрашивай почему). Другой фрагмент просто получает информацию от первого, когда пользователь выполняет некоторые действия. Это достигается просто путем совместного использования активности viewmodel. Но само приложение имеет много бизнес-логики, и по мере продвижения модель представления становится все больше и больше.
То, что я хочу спросить - не квитанция или правила, как это исправить, или, может быть, как преодолеть это, исправив всю структуру проекта. Я хочу попросить совета, как я могу применить подход MVVM в стиле android.arch.lifecycle для моего варианта использования.
Я не видел ничего более сложного, чем просто разделение Activity ViewModel между фрагментами. Но обычно это не лекарство. введите описание изображения здесь

То, что вы можете увидеть здесь - на самом деле беспорядок. Дело в том, что все разделяют ActivityViewModel, Соединения (агрегация) из FirstFragment означают, что ViewPager внутри FirstFragment инициирует ChildFragments и они также работают с тем же ActivityViewModel(убей меня). В результате все работают с одной общей моделью ViewModel.
Мое предложение состоит в том, чтобы добавить ViewModel для каждого слоя. Так что Activity/Fragments/ChildFragments имеют свои собственные ViewModels. Но что здесь появляется - как нам тогда общаться?
Возможные решения:

  • Наличие двух ViewModels для одного компонента. Один ViewModel будет обрабатывать / делегировать бизнес-логику, а другой - устанавливать связь. Две модели на компонент - не очень хорошо, да?
  • Имея старый интерфейс (пожалуйста, нет!)
  • Другие обходные пути - как слушатели изменений DB/SharedPrefs/Realm и шины событий (я слишком стар для этого:().

  • Ваше решение здесь!

Я скажу, что все вышеперечисленное нарушает многие принципы дизайна, так что мне делать? Как я должен выйти из этого беспорядка? Есть ли Uncle Bob или другой superhero здесь, чтобы помочь?

PS - Ну, создание UML или других диаграмм - не моя сильная сторона. Простите за это.
PPS - мне известны образцы Google.

2 ответа

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

Сделай один ViewModel

Скажем MyActivityViewModel обрабатывать всю логику, связанную с уровнем активности. Итак, если какая-либо фрагментная логика напрямую связана с вашей деятельностью, то поделитесь своими ViewModel как ниже:

ViewModelProviders.of(getActivity()).get(MyActivityViewModel.class); // Like this in fragment.

&

ViewModelProviders.of(this).get(MyActivityViewModel.class); // Like this in activity.

Это поделится общим ViewModel между вашей деятельностью и фрагментом.


Другая ViewModel будет идти на FirstFragment в вашем случае, если вы должны разделить логику между ChildFragment:

Здесь вы можете поделиться ViewModel скажем FragmentViewModel как ниже:

ViewModelProviders.of(this).get(FragmentViewModel.class); // Like this in FirstFragment which is having view pager.

&

ViewModelProviders.of(getParentFragment()).get(FragmentViewModel.class); // Like this in View pager fragments, getParentFragment() is First fragment in our case.

Хотя мы все еще можем использовать наш уровень активности MyActivityViewModel в нашем дочернем фрагменте из FirstFragment вроде:

ViewModelProviders.of(getActivity()).get(MyActivityViewModel.class);

Во-первых, нет никакого вреда в наличии нескольких ViewModelэто для одного взгляда.

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

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

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

Это обновленная версия ответа Джила Ванкхеде. А также реализация Kotlin того же самого.

Поскольку ViewModelProviders устарел, мы должны использовать ViewModelProvider.

Вот как вы это делаете в Activity:

ViewModelProvider(this).get(MyActivityViewModel::class.java)

Вот как вы это делаете во фрагменте:

ViewModelProvider(requireActivity()).get(MyActivityViewModel::class.java)

Чтобы решить проблему FirstFragment разделяя свою модель представления с дочерними фрагментами, вы можете использовать этот код для доступа к FirstFragmentViewModel из любого дочернего фрагмента:

    // in ChildFragment1
    val firstFragmentViewModel: FirstFragmentViewModel by viewModels(
        { requireParentFragment() }
    )