2

I want to share a few Integer values from one fragment to the other. I don't want lose the data when the device changes configuration.

So the two ways that I came across and want to know which one will be better for my use case are:

1. Sharing a ViewModel among multiple fragments

class SharedViewModel : ViewModel(){
    ...
}

class FragmentA : Fragment(){
    private val model: SharedViewModel by activityViewModels()
    ...
}

class FragmentB : Fragment(){
    private val model: SharedViewModel by activityViewModels()
    ...
}

2. Using combination of SafeArgs and custom ViewModelProvider.Factory

Using SafeArgs to pass data as a parameter to a navigation action from a fragment (say A) to another fragmet (say B). Implementing ViewModel (parameterized) and ViewModelFactory classes for fragment B. Passing the data from SafeArgs to ViewModelFactory to create a ViewModel (using ViewModelProvider)

Something like this:

class B : Fragment() {

    //Seperate classes for ViewModelB & ViewModelFactoryB
    private lateinit var viewModel: ViewModelB
    private lateinit var viewModelFactory: ViewModelFactoryB

    override fun onCreateView(
            inflater: LayoutInflater,
            container: ViewGroup?,
            savedInstanceState: Bundle?
    ): View? {

        val binding: BFragmentBinding = DataBindingUtil.inflate(
                inflater,
                R.layout.b_fragment,
                container,
                false
        )

        viewModelFactory = ViewModelFactoryB(BFragmentArgs.fromBundle(requireArguments()).data)
        viewModel = ViewModelProvider(this, viewModelFactoryB).get(ViewModelB::class.java)

        return binding.root
    }
}
Vandit Goel
  • 620
  • 2
  • 9
  • 19
  • 1
    When you ask for the best but you don't mention in which circumstances, each upcoming answer may be biased based on how everyone sees it. but I think it is a good start for you to see this page. https://developer.android.com/guide/navigation/navigation-pass-data – Emad Razavi Aug 03 '20 at 19:46
  • I hope my question clearly explains my use case now. Sorry for not framing it properly earlier. – Vandit Goel Aug 04 '20 at 11:44

1 Answers1

3

It totally depends on your use case, what data you share between fragments and how it is used.

Even though both cases support passing custom objects you have to consider a few things.

In the case of the navigation component, regarding custom objects:

  • data must be either Parcelable (preferred) or Serializable;
  • all of the data passed through using Bundle object which has its own limitations and with large data can lead to TransactionTooLargeException (more about it). It can be easily avoided when using shared view model.

In the case of shared view models:

  • you have to define new classes to wrap your models;
  • you have to add new class members to hold shared view models;
  • a shared view model will take memory until Activity is finished or the view model store is cleared.

So what is the answer?

Such a question leads to opinionated answers, but I consider following a set of hints to use when choosing between safe argument and shared view model.

Use safe arguments when:

  1. data is small;
  2. when you do not need to return results to the previous fragment after using this data;
  3. you do not care if this data is removed from memory when you are done with it and you have to query it again each time.

Use a shared view model when:

  1. data is relatively large or you assume it could be large;
  2. you have multiple data models/sets;
  3. when you do need to return results to the previous fragment after using this data;
  4. when data is expensive to query. Shared view models typically live as long as the activity.
Jenea Vranceanu
  • 4,530
  • 2
  • 18
  • 34
  • Sorry for not framing the question properly. I wanted to know how to choose between the two ways and your answer explains just that. Thank you for the answer. – Vandit Goel Aug 04 '20 at 11:37
  • I want to share a few integer values from `FragmentA` to `FragmentB`, so the data which I'm sharing is small. For using **SafeArgs** I'd have to create a `ViewModelFactory` and a `ViewModel` separately for FragmentB and pass the integers from Bundle to ViewModel factory. Although I will avoid all this code if I share a ViewModel between those fragments. What would you suggest me to proceed with? – Vandit Goel Aug 04 '20 at 11:58
  • For safe arguments, you do not need any ViewModel related classes. You need to use the navigation component and in your navigation graph you should define two arguments. [Example of one argument](https://developer.android.com/guide/navigation/navigation-pass-data#override_a_destination_argument_in_an_action). Then in your receiver fragment you can use `MyFragmentArgs.fromBundle(getArguments()).get[variable_name_here]();` to get value passed in. – Jenea Vranceanu Aug 04 '20 at 12:12
  • In order to take care of Configuration changes in my app I'd either have to use `onSaveInstanceState()` or **ViewModel** but since I'm currently learning Architecture Components I'd want to use ViewModel. In that case, I have two ways (that I know) to achieve this as mentioned in the question. – Vandit Goel Aug 04 '20 at 12:21
  • 1
    Then use view models. It will be easier to use [shared view model](https://developer.android.com/topic/libraries/architecture/viewmodel#sharing). There is a small piece of code in that topic by the link. It is all you need to set and access data from one fragment to the other. – Jenea Vranceanu Aug 04 '20 at 12:43