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.

Anand Kumar
  • 1,439
  • 1
  • 15
  • 22
  • 1
    You can have a parent fragment and within that scope you can share the viewmodel with nested child fragments. Something like this: https://github.com/A-Zaiats/Kotlinextensions/blob/a2d0fd45bf8a3e3b41f591d9bf208a9164b8c6ef/arch/src/main/java/io/github/azaiats/kotlinextensions/arch/ViewModel.kt#L21 – denvercoder9 Sep 16 '19 at 08:48

4 Answers4

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.

Shift Delete
  • 1,015
  • 6
  • 13
  • I upped your answer, however, I'm curious what the question poster will comment on this :D – barotia Sep 18 '19 at 12:35
  • https://developer.android.com/guide/navigation/navigation-programmatic#share_ui-related_data_between_destinations_with_viewmodel What do you say about this approach. – Anand Kumar Oct 04 '19 at 02:59
  • 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. – Shift Delete Oct 05 '19 at 13:55
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)

denvercoder9
  • 2,979
  • 3
  • 28
  • 41
  • 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. – Shift Delete Sep 17 '19 at 15:40
  • @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 classes – denvercoder9 Sep 18 '19 at 07:47
  • 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. – Shift Delete Sep 18 '19 at 11:14
  • 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. :-) – Shift Delete Sep 18 '19 at 11:15
  • 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. – denvercoder9 Sep 18 '19 at 12:25
  • 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) . – denvercoder9 Sep 18 '19 at 12:26
  • 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 :) – denvercoder9 Sep 18 '19 at 12:26
  • 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. – Shift Delete Sep 18 '19 at 14:10
  • 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](http://hannesdorfmann.com/android/mosby3-mvi-4). – Shift Delete Sep 18 '19 at 14:11
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.

Vrushi Patel
  • 2,361
  • 1
  • 17
  • 30
-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.
barotia
  • 428
  • 4
  • 12
  • 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. – Anand Kumar Sep 14 '19 at 05:22
  • 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. – barotia Sep 14 '19 at 06:42
  • 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? – Anand Kumar Sep 16 '19 at 04:01
  • 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. – Shift Delete Sep 17 '19 at 15:12