I have a small app where one ViewModel has been shared between Fragment and FragmentDialog. It shares not only logic, but also a state. It works fine until I have added one more fragment which uses the same ViewModel. The issue is when I trigger a method of the ViewModel this method is called, but its viewModelScoped doesn't.
At first I thought it caused by ViewModel's onCleared() call somewhere during runtime. Indeed, onCleared() is called when the first Fragment replaced by the second Fragment. However, when the call of my method is happened ViewModel is still here (not null) and its viewModelScope should be too, isn't it?
Later I noticed a recommendation to pass a parent Activity as a context for a ViewModelFactory - because I have single Activity, ViewModelStoreOwner would be the same for all fragments. It works, but I still concern about root cause of this issue and possible alternative solutions. Single activity is not an always option for many android apps.
public class ViewModelFactory implements ViewModelProvider.Factory {
private final Map<Class<? extends ViewModel>, Provider<ViewModel>> viewModels;
@Inject
public ViewModelFactory(Map<Class<? extends ViewModel>, Provider<ViewModel>> viewModels) {
this.viewModels = viewModels;
}
@Override
public <T extends ViewModel> T create(Class<T> modelClass) {
Provider<ViewModel> viewModelProvider = viewModels.get(modelClass);
if (viewModelProvider == null) {
throw new IllegalArgumentException("model class " + modelClass + " not found");
}
return (T) viewModelProvider.get();
}
}
@Module
public abstract class ViewModelModule {
@Binds
abstract ViewModelProvider.Factory bindViewModelFactory(ViewModelFactory viewModelFactory);
@Binds
@IntoMap
@ViewModelKey(DashboardViewModel.class)
@Singleton
abstract ViewModel dashboardViewModel(DashboardViewModel vm);
}
viewModel = ViewModelProvider(requireActivity(), viewModelFactory).get(DashboardViewModel::class.java)
/*DashboardViewModel class*/ {
fun addItem() {
Log.d(App.TAG, "Add item has started")
viewModelScope.launch {
Log.d(App.TAG, "Add item coroutine has started")
// ...
}
If you have any good thoughts on this, please share.