Assume a view model like this:
public class FooViewModel extends AndroidViewModel {
@Inject public FooViewModel(Application app, SavedStateHandle handle, Bar bar) {
// ...
}
}
I want to inject Bar
using Dagger 2. I am developing on Android.
According to the SavedStateHandle
docs:
You should use
SavedStateViewModelFactory
if you want to receive this object inViewModel
's constructor.
However, the SavedStateViewModelFactory
docs state that the factory is final
which means I cannot inject Bar
there, either.
So far, I have been injecting via a setter:
@Provides
FooViewModel provideFooViewModel(ViewModelStoreOwner owner, Bar bar) {
FooViewModel viewModel = new ViewModelProvider(owner).get(FooViewModel.class);
viewModel.setBar(bar);
return viewModel;
}
Is there a better way to do this?
I want to use constructor injection, mark the Bar
instance variable as final
and eliminate the setter.
To provide a FooViewModel
, you need a custom implementation of AbstractSavedStateViewModelFactory
.
MyComponent component = DaggerMyComponent.withViewModelStoreOwner(this)
.withSavedStateRegistryOwner(this)
.withDefaultArguments(this.arguments != null ? this.arguments : new Bundle())
.build();
and
@Provides
@Suppress("UNCHECKED_CAST")
public MyViewModel viewModel(ViewModelStoreOwner viewModelStoreOwner, SavedStateRegistryOwner savedStateRegistryOwner, Bundle defaultArgs, Application application, Bar bar) {
return new ViewModelProvider(
viewModelStoreOwner,
new AbstractSavedStateViewModelFactory(savedStateRegistryOwner, defaultArgs) {
@Override
public <T extends ViewModel> T create(
String key,
Class<T> modelClass,
SavedStateHandle handle) {
return (T) new MyViewModel(application, handle, bar);
}
}).get(MyViewModel.class);
});
}
Note:
1.) you only get SavedStateHandle
inside the AbstractSavedStateViewModelFactory
, so you won't be able to get it into your graph.
2.) you can reduce the length of that provider by using https://github.com/square/AssistedInject. Theoretically AutoFactory would also work, but it seems unmaintained in comparison.
3.) you won't be able to get @Inject
on your ViewModel.
This answer was partly adapted from https://github.com/Zhuinden/DaggerViewModelExperiment/blob/c3cbf0a5bc85467cec08755fcc152db5e8c55f91/app/src/main/java/com/zhuinden/daggerviewmodelexperiment/features/second/SecondFragment.kt#L32-L47 .