40

I have an architectural question about the android ViewModels:

Let's say that in my App I have an Activity with two Fragments inside (using a Viewpager). The two fragments do different things (therefore may have their own ViewModel?), but they also both need various data that is similar.

This is for example the state if a network connection is available or not (and both fragments show different error UIs if there is no connection), or some user setting that comes via a Push from a server and affects both fragments equally.

This looks something like this:

enter image description here

Now my question is how to deal with that situation when using ViewModels? Is it good that a view observes multiple ViewModels, like it would be if I have a ViewModel for the Activity (holding the state that both need equally) and one for each fragment, like this:

enter image description here

This was hinted here for example, but it is not a good practice, as the relationship in MVVM generally is

View n - 1 ViewModel n - 1 Model

But I am not sure where the right place for such shared LiveData is in my case?

stamanuel
  • 3,731
  • 1
  • 30
  • 45
  • In my opinion, you should have a ViewModel for each Activity/Fragment where you need to perform "business logic" operations. Basically it is in the ViewModel that manipulate the data do be shown in the View or to be saved to a database. – joao86 Nov 23 '17 at 16:47
  • @joao86 yes that I know. But how to deal with states that are necessary for multiple Fragments that are on the same level inside the same activity? – stamanuel Nov 23 '17 at 19:36
  • 1
    you can use one ViewModel for more than one Fragment/Activity, otherwise you would be repeating code and behaviour unnecessarily. – joao86 Nov 24 '17 at 06:16
  • I mentioned this in my question and linked two aspects of this. While it is possible, it is generally considered a bad practice, in MVVM a View should have only one ViewModel. See the links and text in my question. – stamanuel Nov 24 '17 at 06:21
  • 2
    It may be considered a bad pratice but it is also considered a bad pratice to repeat code and behaviour, That is why it exists the concept of super classes in JAVA for example. So which one of the good behaviours should be followed? :) – joao86 Nov 24 '17 at 06:38
  • that are two completely different things. I was asking if anyone knows a better way of doing this while not violating this obvious practice. Or if there is any official example from google on how to deal with it. As stated in my question, I know it is possible, but that does not mean there is no better way – stamanuel Nov 24 '17 at 06:40
  • MVVM has only been introduced in Android "oficially" with the latest architectural components (ViewModel, LiveData, Room) so I don't know if there is a lot of examples on their side. Maybe somebody else can help you other than me. I'll upvote your question. – joao86 Nov 24 '17 at 06:51

2 Answers2

14

Late answer but I asked myself the same question and found the answer in Google guide. Especially for fragments, it is mentioned on Google Documentations explicitly here

class SharedViewModel : ViewModel() {
    val selected = MutableLiveData<Item>()

    fun select(item: Item) {
        selected.value = item
    }
}

class MasterFragment : Fragment() {

    private lateinit var itemSelector: Selector

    // Use the 'by activityViewModels()' Kotlin property delegate
    // from the fragment-ktx artifact
    private val model: SharedViewModel by activityViewModels()

    override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
        super.onViewCreated(view, savedInstanceState)
        itemSelector.setOnClickListener { item ->
            // Update the UI
        }
    }
}

class DetailFragment : Fragment() {

    // Use the 'by activityViewModels()' Kotlin property delegate
    // from the fragment-ktx artifact
    private val model: SharedViewModel by activityViewModels()

    override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
        super.onViewCreated(view, savedInstanceState)
        model.selected.observe(viewLifecycleOwner, Observer<Item> { item ->
            // Update the UI
        })
    }
}
Kaaveh Mohamedi
  • 1,399
  • 1
  • 15
  • 36
kiroglue
  • 341
  • 2
  • 7
12

I think the concept behind the ViewModel was that it is supposed to be related to a single "Screen" rather than a "View". So going by that logic, I think you can use the same ViewModel if multiple fragments reference the same ViewModel because they technically belong to the same "Screen".

In the fragments, you could request the activity for the ViewModel which holds the instance of LiveData and could give you the updates as needed.

Hope this answers your question.

Update: I found a link to a sample fragment in Google samples. Check out onCreateView() method. Pasting code below for reference:

@Nullable
@Override
public View onCreateView(LayoutInflater inflater, ViewGroup container,
                         Bundle savedInstanceState) {
    final View root = inflater.inflate(R.layout.addtask_frag, container, false);
    if (mViewDataBinding == null) {
        mViewDataBinding = AddtaskFragBinding.bind(root);
    }

    mViewModel = AddEditTaskActivity.obtainViewModel(getActivity());

    mViewDataBinding.setViewmodel(mViewModel);

    setHasOptionsMenu(true);
    setRetainInstance(false);

    return mViewDataBinding.getRoot();
}

P.S. If you have found a better solution/answer/practice, lemme know.

karthik prasad
  • 738
  • 7
  • 15
  • Hi @karithik, the sample you have provided, is a different case, if you will checkout the code further, you will see there is One separate activity named `AddEditTaskActivity` and it has a fragment `AddEditTaskFragment` and a single view model binding to both the classes, this is not what the question has specified. – Sanjeev Oct 09 '18 at 08:51
  • 5
    I think this was a more generic question as to - Can I share one viewModel between an Activity and Fragment? And it seems like it's okay to do this if they have a common code or common "state". The code I pasted is just an example. So, in theory, you could have multiple fragments using a common "state" or code from the Activity's ViewModel. Did that answer your comment? – karthik prasad Oct 09 '18 at 18:20