My question is similar to this.
So for instance, I have a LiveData
implementation:
public class CustomLiveData extends LiveData<SomeEvent> {
@Inject
public CustomLiveData(@ActivityContext Context context) {
//....
}
}
that I want to inject into a custom view:
public class CustomView extends View {
@Inject
SomeApplicationProvider anyProvider;
@Inject
CustomLiveData dataProvider;
// Getting @com.di.qualifiers.ActivityContext android.content.Context cannot be provided without an @Provides-annotated method.
// @com.di.qualifiers.ActivityContext android.content.Context is injected at com.repositories.CustomLiveData.<init>(context)
// com.repositories.CustomLiveData is injected at com.ui.CustomView.dataProvider com.ui.CustomView is injected at
// com.di.ApplicationComponent.inject(view)
public CustomView(Context context) { this(context, null); }
public CustomView(Context AttributeSet attrs) {
super(context, attrs);
// Works ok for application provider
Application.getComponent(context).inject(this);
}
}
And here is the rest of DI classes:
@ApplicationScope
@Component(
modules = {AndroidInjectionModule.class,
ActivityBuilder.class
})
public interface ApplicationComponent extends AndroidInjector<MyApp> {
void inject(MyApp application);
void inject(CustomView view);
@Component.Builder
abstract class Builder extends AndroidInjector.Builder<MyApp> {
public abstract ApplicationComponent build();
}
}
@ActivityScope
@Module (subcomponents = MainActivitySubcomponent.class)
public abstract class ActivityBuilder {
@Binds
@IntoMap
@ActivityKey(MainActivity.class)
abstract AndroidInjector.Factory<? extends Activity>
bindActivityInjectorFactory(MainActivitySubcomponent.Builder builder);
//...
}
@Subcomponent(modules = {MainActivityModule.class})
public interface MainActivitySubcomponent extends AndroidInjector<MainActivity> {
@Subcomponent.Builder
abstract class Builder extends AndroidInjector.Builder<MainActivity> {
}
}
@ActivityScope
@Module
public class MainActivityModule {
@Provides
@ActivityContext
public Context provideActivityContext(MainActivity activity) {
return activity;
}
// Seems to be wrong or not enough!?
@Provides
public CustomLiveData provideCustomLiveData(@ActivityContext Context context) {
return new CustomLiveData(context);
}
}
@Qualifier
public @interface ActivityContext{
}
Note, that I don't get any compiler complaints if CustomLiveData
is injected into MainActivity
instead into the view.
Thanks!
tl;dr Don't inject model layer dependencies inside custom
View
objectsSubclasses of
View
are not good targets for Dagger 2 injection.View
objects are meant to be drawn and not must else, hence the name "view". The constructors forView
should make this clear; they are designed for inflatingView
objects from attributes specified in XML. In other words, aView
object should be able to be specified in alayout.xml
file, inflated at the appropriate point in the lifecycle, and then obtained usingfindViewById(int id)
, Butterknife or data binding. In this way, the best customView
objects take no dependencies.If you want to link a
View
and some data from the model layer, the standard pattern is to write an Adapter like those forRecyclerView
andListView
. If this is not possible, using a setter (e.g.,setData()
) is preferable to passing dependencies from the model layer in the constructor or requesting injection from within one of the lifecycle methods of theView
.If instead you inject your
LiveData
object inside an Activity or Fragment using theAndroidInjector
class the correctContext
will be provided without you having to do anything. This explains your comment "I don't get any compiler complaints if CustomLiveData is injected into MainActivity instead into the view."Once you have injected the
LiveData
object into the Activity, use one of the above methods (an adapter or a setter) to associate the data with your customView
. See the Google Android Architecture example here where elements from the model layer are injected using Dagger 2 and then associated with aListView
usingfindViewById
andsetAdapter()
Link to the Dagger 2 issue where injection of
View
objects is discussed:https://github.com/google/dagger/issues/720
Your Dagger hierarchy looks like this:
appcomponent
->activitycomponent
You try to inject activity
context
inside view, that depends onappcomponent
directly.It's not possible since there is no method that could provide activity context in
appcomponent
. Instead, inside view, you should retrieve activity (for example usinggetContext
), extractactivitycomponent
from it and only then injectCustomLiveData
.