Reputation: 109
Reprository : https://github.com/googlesamples/android-architecture
Branch - todo-mvp-dagger
It is found that TaskFragment is injected using contructorinjection
For instance : In the TasksModule, I want to add another module for task fragment like below for field Injection in the TaskFragment
@Module
class TasksModule{
@Fragmentscoped
@contributesandroidinjector(modules = AnotherModule.class)
abstract TasksFragment tasksFragment();
}
@Module
public class AnotherModule {
@Provides
@FragmentScoped
static Calendar getCalendar() {
return Calendar.getInstance();
}
}
@activityscoped
public class TasksFragment extends DaggerFragment implements TasksContract.View {
@Inject
Calendar calendar;//Field injection
@Inject
TasksFragment(){
}
}
Activity :
public class TasksActivity extends DaggerAppCompatActivity {
@Inject
Lazy<TasksFragment> taskFragmentProvider;
....
}
I am getting the error as :
Error:(34, 8) error: [dagger.android.AndroidInjector.inject(T)] java.util.Calendar cannot be provided without an @Provides- or @Produces-annotated method.
java.util.Calendar is injected at
com.example.android.architecture.blueprints.todoapp.tasks.TasksFragment.calendar
dagger.Lazy<com.example.android.architecture.blueprints.todoapp.tasks.TasksFragment> is injected at
com.example.android.architecture.blueprints.todoapp.tasks.TasksActivity.taskFragmentProvider
com.example.android.architecture.blueprints.todoapp.tasks.TasksActivity is injected at
dagger.android.AndroidInjector.inject(arg0)
A binding with matching key exists in component: com.example.android.architecture.blueprints.todoapp.tasks.TasksModule_TasksFragment.TasksFragmentSubcomponent
Am I missing anything here regarding injection?
Upvotes: 0
Views: 1655
Reputation: 95654
To answer your question directly, you are evidently trying to instantiate your TasksFragment from within your ActivityComponent. However, you have bound your Calendar in @FragmentScoped scope within the fragment-specific subcomponent that dagger.android creates for you. This means that Calendar is available only from within your Fragment (and other objects that your Fragment accesses), not from within your Activity.
If you want the right scope and injecting component, you'd use the Fragment's component, which has a generated name and no Fragment-constructing method: It's not designed to be called that way.
The easy answer: don't rely on constructor injection here. You should be calling new
on your Fragment, because that's what Android does; you are required to have a public parameterless constructor specifically for this purpose, and Android will not inject your Fragment upon construction. Though there aren't any special Dagger rules about constructor injection and field injection here, this is a constraint of the Android system and its necessary ability to recreate Fragment instances for you.
Instead, by extending DaggerFragment, you are instructing Dagger to inject your Fragment in its onAttach
method, which is the right way to do it, and the way that dagger.android is designed for. If you were to inject the Fragment earlier, manually or automatically, then those @Inject fields would be replaced and reinjected when the Fragment is attached...and your Fragment would have differing behavior depending on whether Android creates the object for you automatically, or whether you do so yourself.
Other notes:
@Inject Lazy<TasksFragment> taskFragmentProvider
as a Provider: Unlike a general Provider
, a Lazy
will always return the same instance, even if the object is scopeless.onAttach
method where you can use those Bundle arguments with full access to your injected objects.Upvotes: 3