Share ViewModel between fragments that are in diff

2019-02-04 21:46发布

问题:

I have a ViewModel named SharedViewModel:

public class SharedViewModel<T> extends ViewModel {

    private final MutableLiveData<T> selected = new MutableLiveData<>();


    public void select(T item) {
        selected.setValue(item);
    }

    public LiveData<T> getSelected() {
        return selected;
    }
}

I implement it based on SharedViewModel example on the Google's Arch ViewModel reference page:

https://developer.android.com/topic/libraries/architecture/viewmodel.html#sharing_data_between_fragments

It is very common that two or more fragments in an activity need to communicate with each other. This is never trivial as both fragments need to define some interface description and the owner activity must bind the two together. Moreover, both fragments must handle the case where the other fragment is not yet created or not visible.

I have two fragments, called ListFragment and DetailFragment.

Until now I used this two fragments inside a called MasterActivity. And everything worked well.

I got the ViewModel in ListFragment, selected the value to use it on DetailFragment.

mStepSelectorViewModel = ViewModelProviders.of(getActivity()).get(SharedViewModel.class);

However, now I need that In certain cases that ListFragment (a layout to a different device configuration) be added to a different activity, called DetailActivity. Is there a way to do that similarly the above example?

回答1:

A little late but you can accomplish this using a shared ViewModelStore. Fragments and activities implement the ViewModelStoreOwner interface. In those cases fragments have a store per instance and activities save it in a static member (I guess so it can survive configuration changes).

Getting back to the shared ViewModelStore, let say for example that you want it to be your Application instance. You need your application to implement ViewModelStoreOwner.

class MyApp: Application(), ViewModelStoreOwner {
    private val appViewModelStore: ViewModelStore by lazy {
        ViewModelStore()
    }

    override fun getViewModelStore(): ViewModelStore {
        return appViewModelStore
    }
}

Then in the cases when you know that you need to share ViewModels between activity boundaries you do something like this.

val viewModel = ViewModelProvider(myApp, viewModelFactory).get(CustomViewModel::class.java)

So now it will use the Store defined in your app. That way you can share ViewModels.

Very important. Because in this example the ViewModels live in your application instance they won't be destroyed when the fragment/activity that uses them gets destroyed. So you will have to link them to the lifecycle of the last fragment/activity that will use them, or manually destroy them.



回答2:

you can use factory to make viewmodel and this factor will return single object of view model.. As:

class ViewModelFactory() : ViewModelProvider.Factory {

override fun create(modelClass: Class): T {
    if (modelClass.isAssignableFrom(UserProfileViewModel::class.java)) {
    val key = "UserProfileViewModel"
    if(hashMapViewModel.containsKey(key)){
        return getViewModel(key) as T
    } else {
        addViewModel(key, UserProfileViewModel())
        return getViewModel(key) as T
    }
    }
    throw IllegalArgumentException("Unknown ViewModel class")
}

companion object {
    val hashMapViewModel = HashMap<String, ViewModel>()
    fun addViewModel(key: String, viewModel: ViewModel){
        hashMapViewModel.put(key, viewModel)
    }
    fun getViewModel(key: String): ViewModel? {
        return hashMapViewModel[key]
    }
}
}

In Activity:

viewModelFactory = Injection.provideViewModelFactory(this)

// Initialize Product View Model
userViewModel = ViewModelProviders.of(this, viewModelFactory).get(
UserProfileViewModel::class.java)`

This will provide only single object of UserProfileViewModel which you can share between Activities.



回答3:

If you want a ViewModel that is shared by all your activities (as opposed to some), then why not store what you want stored in that ViewModel inside your Application class?

The trend presented at the last Google I/O seems to be to abandon the concept of Activities in favor of single-activity apps that have a lot of Fragments. ViewModels are the way to remove the great number of interfaces the activity of an interface formerly had to implement. Thus this aproach no longer makes for giant and unmaintainable activities.



回答4:

I think we still get confused with the MVVM framework on Android. For another activity, do not get confused because it must necessarily be the same, why?

This makes sense if it has the same logic (even if the logic could still be abstract in other useful classes), or if the view in the XML is almost identical.

Let's take a quick example:

I create a ViewModel called vmA, and an activity called A and I need the user's data, I will go to insert the repository in vmA of the User.

Now, I need another activity that needs to read user data, I create another ViewModel called vmB and in it I will call the user repository. As described, the repository is always the same.

Another way already suggested is to create N instances of the same ViewModel with the implementation of the Factory.



回答5:

Here's a link

Hope it helps you. O(∩_∩)O~

In addition:

1) The inspiration for the code came from smart pointer in c++.

2) It will be auto cleared when no activities or fragments references ShareViewModel. The ShareViewModel # onShareCleared() function will be called at the same time! You don't need to destroy them manually!

3) If you use dagger2 to inject the ViewModelFactory for share the viewmodel
between two activities (maybe three), Here's sample