相关文章推荐
逆袭的圣诞树  ·  Android ...·  1 周前    · 
曾深爱过的大白菜  ·  包小艳·  1 月前    · 
犯傻的芹菜  ·  mysql ...·  3 月前    · 
腹黑的铅笔  ·  打开word时显示microsoft ...·  1 年前    · 
幸福的开心果  ·  javascript - ...·  1 年前    · 
Collectives™ on Stack Overflow

Find centralized, trusted content and collaborate around the technologies you use most.

Learn more about Collectives

Teams

Q&A for work

Connect and share knowledge within a single location that is structured and easy to search.

Learn more about Teams

The problem is quite straightforward. The question is in context of using ViewModels, LiveData and other related Lifecycle aware arch approaches.
I have an Activity with NavDrawer, which switches fragments inside.
And also I have a case when two fragments are present at the same time on the screen - this will be the main pain. One Fragment has a ViewPager with nested Fragments ( don't ask why ). The other fragment is just obtaining info from first one when user performs some actions. This is achieved just by sharing activity viewmodel. But the app itself has a lot of business logic and as it goes further the viewmodel goes bigger and bigger.
What I want to ask - not a receipt or rules how to fix this, or maybe how to overcome this by fixing the entire structure of the project. I want to ask for suggestions how can I apply the MVVM approach within android.arch.lifecycle style to mine use-case.
I haven't seen something more complicated then just sharing the Activity ViewModel between Fragments. But common, that's not a cure.

What you can see here - a mess actually. The point is that all are sharing the ActivityViewModel . Connections(aggregation) from FirstFragment mean that ViewPager inside FirstFragment is initiating ChildFragments and they are also working with the same ActivityViewModel (kill me). So as result everyone is working with one shared ViewModel.
My proposal is to add a ViewModel for each Layer. So that Activity/Fragments/ChildFragments have their own ViewModels. But what appears here - how we should communicate then ?
Possible solutions :

  • Having two ViewModels per one component. One ViewModel will handle/delegate the business logic and another will make the communication. Two viewmodels per component - not so good, yeah?
  • Having old manner interface( please no! )
  • Other workarounds - like DB/SharedPrefs/Realm change listeners and Event Buses( I'm too old for this :( ).

  • Your solution here!

  • I'll say that all of the above are breaking a lot of design principles, so what should I do? How should I come out of this mess? Is there any Uncle Bob or another superhero here to help?

    P.S. - Well, creating UMLs or other charts isn't mine forte. Sorry for that.
    P.P.S. - I'm aware of google samples .

    What's wrong with having multiple ViewModels per component? They are keyed by class name for a reason. ianhanniballake Sep 17, 2017 at 21:01 @ianhanniballake Hm, my thoughts on that are controversial. Always was trying to bind only one VM to one component. Lets say VM per component. github.com/googlesamples/android-architecture-components/issues/… - not the same, but viewmodel per screen. Yurii Tsap Sep 17, 2017 at 21:10 when i had many viewmodels, all communication I had made through streams and changing variables in shared managers/models ex. first fragment changes input to 6 and sets it in manager (model) and manager using observable pattern (in your case it may be mutablelivedata) notify observers (other fragments observing live data variable) that value has changed V-master Oct 5, 2017 at 13:13 @V-master Sure, but in such case I need to have a reference to model/manager. So I'll reference two viewmodels in my fragment. Yurii Tsap Oct 5, 2017 at 13:18

    What i would suggest you can do is handle two ViewModel for your entire use case.

    Make one ViewModel

    Let's say MyActivityViewModel to handle all logic related for activity level. So, if any fragment logic is directly related to your activity then share your ViewModel like below :

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

    Another ViewModel would go for FirstFragment in your case if you have to share logic between your ChildFragment :

    Here you can share ViewModel let's say FragmentViewModel like below:

    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.
    

    Although, we can still use our activity level MyActivityViewModel in our child fragments from FirstFragment like :

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

    First there is no harm in having multiple ViewModel's for a single View.

    I would think about my ViewModel's like what kind of data is getting and manipulating, and group them in a way, that seems natural.

    For your case, if the fragments and the activity's logic is very similar, I think you can go with a single ViewModel, but I would avoid that.

    What I would do is break the activity's ViewModel into smaller parts and reuse the proper ViewModel's in my Fragments, so that I wouldn't have a God ViewModel, nor roughly the same code in different ViewModel's.

    This is updated version of answer given by Jeel Vankhede. And also Kotlin implementation of the same.

    Since ViewModelProviders is deprecated now we have to use ViewModelProvider.

    Here is how you do it in Activity:

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

    Here is how you do in Fragment:

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

    To solve the problem of FirstFragment sharing its view model with its child fragments, you can use this code to access the FirstFragmentViewModel from any of the child fragments:

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

    Thanks for contributing an answer to Stack Overflow!

    • Please be sure to answer the question. Provide details and share your research!

    But avoid

    • Asking for help, clarification, or responding to other answers.
    • Making statements based on opinion; back them up with references or personal experience.

    To learn more, see our tips on writing great answers.