Why am I not using SharedViewModel for fragments?

Habr, hello!

The task of organizing the interaction between fragments is very common. At first glance, ShareViewModel is great for this. We create a ViewModel with owner = our activity, in which our fragments are displayed, and get this ViewModel inside each fragment. Because the owner of the ViewModel is activity, the fragments get the same instance of the ViewModel, which allows them to exchange data, call methods, etc. Here is the link from the documentation .

The figure below shows the interaction scheme of 3 fragments.

image

Those. what we do: in each fragment we get the SharedViewModel of those fragments with which we need to interact ...

And this is not the best solution, in my opinion. Because:

  1. *VM. ViewModel View. ViewModel LiveData, View, .. ViewModel - , View.
  2. SharedViewModel — . , SharedViewModel , , . - .
  3. SharedViewModel, . , , .

SharedViewModel can be used only when there are several completely identical fragments with the same data. In my practice, there was no such scenario. There may be two identical fragments, but the state of the ViewModel of each is different, the SharedViewModel is invalid for them.

SharedViewModel brings a ton of problems. You start to get confused, everything is overgrown with dependencies, the code is complicated. It is much easier to work when Fragment + ViewModel is a black box.

What solution can be offered?

A simple service with which each fragment interacts in its own way.

image

This service must be passed inside each ViewModel. Thanks to DI and scope (Dagger, Koin, any others) this can be done easily and simply. If the service should live in the context of an Activity, create it in the Scope of this Activity and pass it to each ViewModel.

See what happened: we removed all SharedViewModel from fragments. Each fragment now knows nothing about the other fragments. If we want to add a new fragment to this scheme, then we only need to connect viewModel D with our service and that’s it. If the fragment should respond to a change in some property of the service - just subscribe to it. We no longer need to understand other people's ViewModels. And this service will be much simpler than any ViewModel, it will be easier to understand the service.

It may seem that there are not so many interacting fragments on one screen. On the phone - maybe. On the tablet - you can’t say that anymore. And FragmentContainerView helps to use fragments in any layouts, which is very convenient if the application has many common components with a life cycle.

Bonus: how do I create fragments?


Like this: SomeFragment ()

And what about the parameters?

With the advent of ViewModel, I do not use parameters. I don’t pass anything to the fragment via bundle (Android Studio still offers to create a fragment with arguments and create a fragment through the static method), I do not use Safety NavArgs for the Navigation Component .

All these things make the fragment conditioned. We cannot just take such a fragment and add it to the ViewPager, for example. One must think how to pass the parameters. Or another example: you created a fragment with parameters, work in it, the data has changed, the fragment already shows something else. We changed the device configuration (turned over), the fragment was created anew with outdated parameters, while the ViewModel already contains other data. And the actualization of the parameters, etc. begins.

I removed all this in the ViewModel. I use the same service through which I pass the parameters. It works like Safety NavArgs. ViewModel requests parameters from the service by the type of the parameter class. After successfully retrieving the parameter, the service deletes them (works like Push / Pop).

This immediately solves the problem of changing the configuration: the fragment was created again, and the ViewModel is already with the current data, all that is needed is to connect them with the View.

There is also a problem when passing parameters through bundle - this is an excess of the permissible amount. If the amount of data transferred is over 500 kb., Then the application may crash due to an exception inside Binder.java. This led to the creation of different libraries that transmit parameters via SD, which is not very fast compared to using RAM.

In general, the tools are cool, everything is flexible, but you need to be careful.

That's all, thanks for watching!

All Articles