I'm trying to add the new Architecture Components ViewModel
to my application while injecting them with dagger. I based my code on what google showed here. I'm trying to avoid having a ViewModelFactory
for each ViewModel
type, so I used the ViewModelFactory
that receives Map<Class<? extends ViewModel>, Provider<ViewModel>> creators
. It works for ViewModels
that have dependencies with @Singleton
scope. However, one of my ViewModels
has a dependency that comes from the fragment. This is the module of that fragment:
@Module
public abstract class DownloadIssueDialogFragmentModule {
@Binds
abstract DialogFragment dialogFragment(DownloadIssueDialogFragment dialogFragment);
@Provides
@FragmentScope
static Issue provideIssue(DownloadIssueDialogFragment dialogFragment) {
return dialogFragment.getIssue();
}
}
And my ViewModelModule
:
@Module
public abstract class ViewModelModule {
@Binds
abstract ViewModelProvider.Factory bindViewModelFactory(ViewModelFactory factory);
@Binds
@IntoMap
@ViewModelKey(DownloadIssueViewModel.class)
abstract ViewModel bindDownloadIssueViewModel(DownloadIssueViewModel viewModel);
}
dagger says it can't provide Issue
. It makes sense since Map<Class<? extends ViewModel>, Provider<ViewModel>>
seems to be created at compile time. But I will only know the parameter during the scope of that fragment. How can I achieve this?
Thank you.
EDIT:
In the end I went with a different approach. Now I create a factory for each ViewModel and instead of injecting ViewModels, I inject the factory.
I created this library: AutoViewModelFactory
To automatically generate the factories. It's the best solution I've found so far.
Since Android Architecture Component ViewModels have a greater scope (read more persistent lifecycle) than Fragments you should avoid making the ViewModel depend on the field from the Fragment.
However, if the Issue
is only known at runtime and is generated by logic in the Fragment you may be able to escape the small dependency cycle issue by using the Holder pattern.
This has been discussed in some other Dagger 2 StackOverflow questions but you would simply define a Java bean with public accessors/mutators:
class IssueHolder {
private Issue issue;
@Inject
IssueHolder() {} //empty explicit constructor as required by Dagger 2
public void setIssue(@Nullable Issue issue) {
this.issue = issue;
}
@Nullable
public Issue getIssue() {
return issue;
}
}
Then you can make your ViewHolder depend on IssueHolder
rather than directly on Issue
:
@Inject IssueHolder issueHolder;
public void doSomething() {
if (issueHolder.get() == null) {
throw new IllegalStateException("Expected IssueHolder to be set by IssueFragment at this point");
}
//TODO: the logic you want here
}
As like any pattern, this Holder pattern should be used sparingly as it can easily degenerate. The best solution is, if possible, to design your modules and dependencies in such a way so as to eliminate the possibility of cycles,
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With