Should we Use ViewModels to communicate between 2 different fragments or bundles in single activity App Architecture?
Asked Answered
I

4

16

Scenario 1 - If we use ViewModels to communicate between fragments, then the ViewModel has to be created by activity reference and hence going to stay there in memory until the activity is destroyed.

Scenario 2 - In master-detail flow ViewModel makes our life easier but again the memory usage issue is there.

Scenario 3 - We have viewModelScope in the new version of arch library to cancel jobs with Fragment/Activity lifecycles, but if ViewModel is created with activity reference then it's going to stay there until activity is destroyed. Hence the job can still be executing and fragment is already gone.

Iene answered 13/9, 2019 at 3:53 Comment(1)
You can have a parent fragment and within that scope you can share the viewmodel with nested child fragments. Something like this: github.com/A-Zaiats/Kotlinextensions/blob/…Bertina
R
11

You can use ViewModels to communication between two different fragments(aka SharedViewmodels) because it's simple but it's not perfect.

As you know the SharedViewModels must be alive until the first joint parent (activity or fragment) is alive.

but wait ...

What is the purpose of ViewModels?

Are we create ViewModels just for communication between fragments? Absolutely not.

Is the use of ViewModels against the purpose of ViewModels? no, I say it's not perfect use them for communication between fragments but if you have a small project you can use them because it's simple.

So what can I do instead of using ViewModels for communication between fragments? You can build independent UI components and use them for communication between fragments.

What is the advantage of building this weird independent UI components? In this way, each component has its own ViewModel (or Presenter) and they haven't any parent/child relation; Instead, they updated from the same business logic or in reactive programming they are just observing the same Model.

What is the meaning of building independent UI components and how to build them? if you are already familiar with reactive programming, I recommend reading this amazing blog post by Hannes Dorfmann; If you don't, I simply propose using EventBus library for communication between fragments but You will soon realize that too much usage of this library leads to spaghetti code.

Ratib answered 17/9, 2019 at 15:3 Comment(3)
I upped your answer, however, I'm curious what the question poster will comment on this :DCoh
developer.android.com/guide/navigation/… What do you say about this approach.Iene
I can't go through the details but in summary, I think this approach is cleaner than your scenarios (when you share your view model via activity) but not better or performant than that.Ratib
B
5

Scenarios

If the fragments are not part of a flow/group, then don't share the ViewModel, just pass some id/data to the new fragment, create its own viewmodel, and query the data for the fragment from its own viewmodel.

If the fragments are part of some flow/group (cart/checkout/booking flow, multi-screen registration process, viewpager fragments, etc) and the logic is common enough, then share the viewmodels between the fragments. In single-activity architecture, I put these flow/process in its own root parent fragment that acts as a host and is used to create the scope of the viewmodel. For example:

MainActivity ->
  -> RootAuthFragment
     -> SplashFragment (replace with below)
     -> LoginFragment (add to backstack with below or onsuccess login go to MainFragment)
     -> SignupFragment (onsuccess go to Main)
  -> MainFragment (replace with RootAuthFragment)

In the above scenario, you can share the viewmodel between login and signup screens with RootAuthFragment's scope. If you have a multi-screen signup process, then you could move that into separate root fragment and create a separate viewmodel for the signup flow.

Bundle vs ViewModels:

Bundles are used to pass some values. So, use it just for that. I use bundles to usually pass primitive data types or enums and based on that I query the actual data from the viewmodel (through android room or retrofit) or if the data objects are small enough, I make them parcelable and just pass that.

If you have a shared ViewModel and it's becoming a god class and does a lot of different things, then it means those fragments need separate ViewModels. Don't share the ViewModel just for data. Share the ViewModel for the common/shared behaviour/data/logic (whichever makes sense for your particular use cases)

Bertina answered 16/9, 2019 at 9:36 Comment(9)
Creating nested fragments just because of communicating between fragments!!! It's not wrong but it has (or may lead to) so many performance issues. Nested fragments increase hierarchy of our view. Nested fragments increase overdrawing probability. Thay may lead to complex code when the code is growing (especially in single activity architecture) and you still need to save all ViewModel in memory just for communicating between fragments instead of saving just model.Ratib
@ShiftDelete I did not say to "save all ViewModel in memory". I wrote: I use bundles to usually pass primitive data types or enums and based on that I query the actual data from the viewmodel (through android room or retrofit) which is essentially what you mean by "saving just model". As for the rest of the stuff, if things are designed and architected properly, and there is a clear separation of concerns, fragments won't become god classesBertina
Suppose that you have one parent fragment(f1) and two sibling fragments(f2, f3). f2 have an event from the internet and you must send it to f1 and f3. You pass event to f1 from getParentFragment().setArgument(bundle) and then you pass it to from f1 to f3 by getChildFragmentManger().findFragmentById().setArgument(bundle). Is this a clean way? What about when we have 3k event(Bundles cant hold more than 2k)? What about lifecycle handling of f1 and f3 (suppose f3 has already died)? As you said, the use of bundles is very limited.Ratib
In must of the cases we need another way of communication and one of them is SharedViewModels. but in this way, we need to save all ViewModel in memory just for communicating between fragments instead of saving just model(or state, or event) in memory (no saving it in disk or no request it again from the internet). So this way also is not perfect for all situations. :-)Ratib
Replying to your comment regarding f1, f2, f3 example: f2 shouldn't send any "events" to any fragments. The event "from the internet" comes through the viewmodel which is shared. If on some user interaction (in f2), that data needs to be "sent" somewhere, f2 will send it to viewmodel: viewmodel.onFavoriteToggled(somedata). ViewModel would fire appropriate events like observable.onNext(Events.FavoriteToggled(somedata)). Anyone subscribed to this observable will get the appropriate events and data. Fragments should only know about each other to instantiate them. <contd.>Bertina
<contd.> Parent fragment knows about child because parent needs to instantiate the child. Siblings don't need to know about each other. This way, if f3 is dead, no problem, because no one is referencing f3 directly. f3 subscribes to the observable exposed from the sharedviewmodel and gets notified of appropriate events. As for sending lots of data through bundles, that's not what I would do. As I said, send minimal data through bundles that is sufficient to query back the full data through viewmodel (through room or retrofit) <end first reply>.Bertina
Replying to your second comment: "we need to save all viewmodel". What do you mean by all? Once the login/signup flow is done and user goes into the main/home screen, the root fragment of the auth flow is destroyed and so is the sharedviewmodel of that flow. There is no holy grail architecture that fits all. So use whichever fits best for your project :)Bertina
You're so close. In your example We don't have any bundles, right? But a little difference already exists. In your example you must delegate observable.onNext(Events.FavoriteToggled(someData)) to your logic, not your ViewModels.Ratib
The main purpose of ViewModels is to interact with logic not interact between themselves. When you remove this behavior from your ViewModels and add it to your logic layer then you have a simple class (we name it a model or a state or an event or whatever name) that common between fragments(not ViewModels anymore) and also we get rid of the nested fragments and their issues. For more details, you can read this.Ratib
M
1

I prefer you should use View Models approach if you are using single activity architecture. To justify my answer I will clear your scenarios here.

  • Scenario 1 - If we use ViewModels to communicate between fragments, then the ViewModel has to be created by activity reference and hence going to stay there in memory until the activity is destroyed.

  • Scenario 2 - In master-detail flow ViewModel makes our life easier but again the memory usage issue is there.

As for memory you are already holding information into memory there is no escaping there. If you don't need data for stay there then you can clear data from models also but again it will kill the purpose of storing data in the first place.

If you pass data using bundle it's also going to take memory there also.

  • Scenario 3 - We have viewModelScope in the new version of arch library to cancel jobs with Fragment/Activity lifecycles, but if ViewModel is created with activity reference then it's going to stay there until activity is destroyed. Hence the job can still be executing and fragment is already gone.

That's the main purpose of using view models it will store the last state for user where he left.

Moue answered 17/9, 2019 at 7:9 Comment(0)
C
-1

As per https://developer.android.com/topic/libraries/architecture/viewmodel states

This approach offers the following benefits:

The activity does not need to do anything, or know anything about this communication.

Fragments don't need to know about each other besides the SharedViewModel contract. If one of the fragments disappears, the other one keeps working as usual.

Each fragment has its own lifecycle, and is not affected by the lifecycle of the other one. If one fragment replaces the other one, the UI continues to work without any problems.
Coh answered 13/9, 2019 at 7:38 Comment(4)
The question is whether or not. Or go with bundles. As in single activity app architecture the view model will remain in memory until activity is killed.Iene
Ok, I thought it's clear, if official docs advices then probably you should do it. In a single activity app architecture itt Will remain in memory, but this won't cause any issue in this case. I've used viewmodels to share data like notification count and it is worked like a charm, without memory issue, of course like anything, this can be overdone too.Coh
In my app I have multiple fragments and hence respective ViewModels for them. I can't have One ViewModel for all fragments. And If I create a ViewModel Just to share data between fragments, I will again end up with a big ViewModel as all my fragments share some data between them. So the question again pops-up bundle or ViewModels?Iene
As I know in MVVM, View and ViewModels have one to many relations. so you can simply split your ViewModel into two or three ViewModels with different lifecycle and prevent from creating a god class.Ratib

© 2022 - 2024 — McMap. All rights reserved.