Android lifecycle library ViewModel using dagger 2 Android lifecycle library ViewModel using dagger 2 android android

Android lifecycle library ViewModel using dagger 2


You need to implement your own ViewModelProvider.Factory. There is an example app created by Google demonstrating how to connect Dagger 2 with ViewModels. LINK. You need those 5 things:

In ViewModel:

@Injectpublic UserViewModel(UserRepository userRepository, RepoRepository repoRepository) {

Define annotation:

@Documented@Target({ElementType.METHOD})@Retention(RetentionPolicy.RUNTIME)@MapKey@interface ViewModelKey {    Class<? extends ViewModel> value();}

In ViewModelModule:

@Moduleabstract class ViewModelModule {    @Binds    @IntoMap    @ViewModelKey(UserViewModel.class)    abstract ViewModel bindUserViewModel(UserViewModel userViewModel);

In Fragment:

@InjectViewModelProvider.Factory viewModelFactory;@Overridepublic void onActivityCreated(@Nullable Bundle savedInstanceState) {            userViewModel = ViewModelProviders.of(this, viewModelFactory).get(UserViewModel.class);

Factory:

@Singletonpublic class GithubViewModelFactory implements ViewModelProvider.Factory {    private final Map<Class<? extends ViewModel>, Provider<ViewModel>> creators;    @Inject    public GithubViewModelFactory(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);        }    }}


Today I learnt a way to avoid having to write factories for my ViewModel classes:

class ViewModelFactory<T : ViewModel> @Inject constructor(    private val viewModel: Lazy<T>) : ViewModelProvider.Factory {    @Suppress("UNCHECKED_CAST")    override fun <T : ViewModel?> create(modelClass: Class<T>): T = viewModel.get() as T}

EDIT: As pointed out by @Calin in the comments, we are using Dagger's Lazy in the code snippet above, not Kotlin's.

Rather than injecting the ViewModel, you can inject a generic ViewModelFactory into your activities and fragments and obtain an instance of any ViewModel:

class MyActivity : Activity() {    @Inject    internal lateinit var viewModelFactory: ViewModelFactory<MyViewModel>    private lateinit var viewModel: MyViewModel    override fun onCreate(savedInstanceState: Bundle?) {        AndroidInjection.inject(this)        super.onCreate(savedInstanceState)        this.viewModel = ViewModelProviders.of(this, viewModelFactory)            .get(MyViewModel::class.java)        ...    }    ...}

I used AndroidInjection.inject(this) as with the dagger-android library, but you can inject your activity or fragment the way you prefer. All that is left is to make sure you provide your ViewModel from a module:

@Moduleobject MyModule {    @JvmStatic    @Provides    fun myViewModel(someDependency: SomeDependency) = MyViewModel(someDependency)} 

Or applying the @Inject annotation to its constructor:

class MyViewModel @Inject constructor(    someDependency: SomeDependency) : ViewModel() {    ...}


I believe there is a second option if you don't want to use the factory mentioned in Robert's answer. It is not necessarily better solution but it is always good to know the options.

You can leave your viewModel with default constructor and inject your dependencies just as you do in case of activities or other elements created by system.Example:

ViewModel:

public class ExampleViewModel extends ViewModel {@InjectExampleDependency exampleDependency;public ExampleViewModel() {    DaggerExampleComponent.builder().build().inject(this);    }}

Component:

@Component(modules = ExampleModule.class)public interface ExampleComponent {void inject(ExampleViewModel exampleViewModel);}

Module:

@Modulepublic abstract class ExampleModule {@Bindspublic abstract ExampleDependency bindExampleDependency(ExampleDependencyDefaultImplementation exampleDependencyDefaultImplementation);}

Cheers,Piotr