How can I create ViewModel and inject repository to it with dagger 2?
ViewModel
is created via ViewModelProvider
that uses a ViewModelFactory
to create the instances. You can't inject ViewModels directly, instead you should use a custom factory like below
@Singleton
public class DaggerViewModelFactory implements ViewModelProvider.Factory {
private final Map<Class<? extends ViewModel>, Provider<ViewModel>> creators;
@Inject
public DaggerViewModelFactory(Map<Class<? extends ViewModel>, Provider<ViewModel>> creators) {
this.creators = creators;
}
@SuppressWarnings("unchecked")
@Override
public <T extends ViewModel> T create(Class<T> modelClass) {
Provider<? extends ViewModel> creator = creators.get(modelClass);
if (creator == null) {
for (Map.Entry<Class<? extends ViewModel>, Provider<ViewModel>> entry : creators.entrySet()) {
if (modelClass.isAssignableFrom(entry.getKey())) {
creator = entry.getValue();
break;
}
}
}
if (creator == null) {
throw new IllegalArgumentException("unknown model class " + modelClass);
}
try {
return (T) creator.get();
} catch (Exception e) {
throw new RuntimeException(e);
}
}
}
Then you need a module for the dagger that creates the view model factory and view models itself.
@Module
abstract class ViewModelModule {
@Binds
abstract ViewModelProvider.Factory bindViewModelFactory(DaggerViewModelFactory factory);
@Binds
@IntoMap
@ViewModelKey(VideoListViewModel.class)
abstract ViewModel provideVideoListViewModel(VideoListViewModel videoListViewModel);
@Binds
@IntoMap
@ViewModelKey(PlayerViewModel.class)
abstract ViewModel providePlayerViewModel(PlayerViewModel playerViewModel);
@Binds
@IntoMap
@ViewModelKey(PlaylistViewModel.class)
abstract ViewModel providePlaylistViewModel(PlaylistViewModel playlistViewModel);
@Binds
@IntoMap
@ViewModelKey(PlaylistDetailViewModel.class)
abstract ViewModel providePlaylistDetailViewModel(PlaylistDetailViewModel playlistDetailViewModel);
}
ViewModelKey
file is like this
@Target({ElementType.METHOD})
@Retention(RetentionPolicy.RUNTIME)
@MapKey
@interface ViewModelKey {
Class<? extends ViewModel> value();
}
Now to get your view model in the activity or fragment simply inject the view model factory and then use that factory to create the view model instances
public class PlayerActivity extends BaseActivity {
@Inject DaggerViewModelFactory viewModelFactory;
PlayerViewModel viewModel;
@Override
protected void onCreate(Bundle savedInstanceState) {
AndroidInjection.inject(this);
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_player);
viewModel = ViewModelProviders.of(this,viewModelFactory).get(PlayerViewModel.class);
}
To inject anything to your ViewModel like the repository simply use Constructor Injection.
public class PlayerViewModel extends ViewModel {
private VideoRepository videoRepository;
private AudioManager audioManager;
@Inject
public PlayerViewModel(VideoRepository videoRepository, AudioManager audioManager) {
this.videoRepository = videoRepository;
this.audioManager = audioManager;
}
}
Check out the fully working example from here https://github.com/alzahm/VideoPlayer, Also I learned many of the dagger stuff from google samples, you can check them out as well.
Dagger2 required you to create ViewModelModule
and do the binding to your ViewModels
I know you are asking about Dagger2, but if you are starting a new project, maybe you can check out Koin
which provides a lightweight injection.
I've used it in some of my production apps and it works fine with lesser lines of code.
You can just declare in your module like
viewModel { MyViewModel(get()) }
Then in your activities / fragments / classes (you need to extend KoinComponent
), just write
val myViewModel : MyViewModel by viewModel()
It will handle the creation itself.
For more information can refer
https://insert-koin.io/
https://start.insert-koin.io/#/getting-started/koin-for-android?id=android-viewmodel