Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Android LiveData - how to reuse the same ViewModel on different activities?

Example ViewModel:

public class NameViewModel extends ViewModel {     // Create a LiveData with a String     private MutableLiveData<String> mCurrentName;      public MutableLiveData<String> getCurrentName() {         if (mCurrentName == null) {             mCurrentName = new MutableLiveData<>();         }         return mCurrentName;     }  } 

Main activity:

mModel = ViewModelProviders.of(this).get(NameViewModel.class);  // Create the observer which updates the UI. final Observer<String> nameObserver = textView::setText;  // Observe the LiveData, passing in this activity as the LifecycleOwner and the observer. mModel.getCurrentName().observe(this, nameObserver); 

I want to call mModel.getCurrentName().setValue(anotherName); in second activity and make MainActivity receive changes. Is that possible?

like image 656
user1209216 Avatar asked Mar 19 '18 13:03

user1209216


People also ask

How do I use the same ViewModel in different activities?

In android, we can use ViewModel to share data between various fragments or activities by sharing the same ViewModel among all the fragments and they can access everything defined in the ViewModel. This is one way to have communication between fragments or activities.

Can two activity share same ViewModel?

You can't share a ViewModel across Activities. That's specifically one of the downsides of using multiple activities as per the Single Activity talk.

How is ViewModel retained?

ViewModel objects are automatically retained during configuration changes so that data they hold is immediately available to the next activity or fragment instance. FYI: You can use ViewModel to preserve UI state only during a configuration change, nothing else as explained perfectly in this official doc.

Can activity and fragment share ViewModel?

These fragments can share a ViewModel using their activity scope to handle this communication.


2 Answers

When you call ViewModelProviders.of(this), you actually create/retain a ViewModelStore which is bound to this, so different Activities have different ViewModelStore and each ViewModelStore creates a different instance of a ViewModel using a given factory, so you can not have the same instance of a ViewModel in different ViewModelStores.

But you can achieve this by passing a single instance of a custom ViewModel factory which acts as a singleton factory, so it will always pass the same instance of your ViewModel among different activities.

For example:

public class SingletonNameViewModelFactory extends ViewModelProvider.NewInstanceFactory {       NameViewModel t;      public SingletonNameViewModelFactory() {       //  t = provideNameViewModelSomeHowUsingDependencyInjection     }      @Override     public NameViewModel create(Class<NameViewModel> modelClass) {         return t;     } } 

So what you need is to make SingletonNameViewModelFactory singleton (e.g. using Dagger) and use it like this:

mModel = ViewModelProviders.of(this,myFactory).get(NameViewModel.class); 

Note:

Preserving ViewModels among different scopes is an anti-pattern. It's highly recommended to preserve your data-layer objects (e.g. make your DataSource or Repository singleton) and retain your data between different scopes (Activities).

Read this article for details.

like image 89
Saeed Masoumi Avatar answered Oct 01 '22 18:10

Saeed Masoumi


When getting the view model using the ViewModelProviders you are passing as lifecycle owner the MainActivity, this will give the view model for the that activity. In the second activity you will get a different instance of that ViewModel, this time for your second Activity. The second model will have a second live data.

What you can do is maintain the data in a different layer, like a repository, which may be a singleton and that way you can use the same view model.

enter image description here

public class NameViewModel extends ViewModel {     // Create a LiveData with a String     private MutableLiveData<String> mCurrentName;      public MutableLiveData<String> getCurrentName() {         if (mCurrentName == null) {             mCurrentName = DataRepository.getInstance().getCurrentName();         }         return mCurrentName;     } }  //SingleTon public class DataRepository           private MutableLiveData<String> mCurrentName;      public MutableLiveData<String> getCurrentName() {         if (mCurrentName == null) {             mCurrentName = new MutableLiveData<>();         }         return mCurrentName;     } //Singleton code ... } 
like image 41
TotoliciCristian Avatar answered Oct 01 '22 17:10

TotoliciCristian