使用dagger 2进行CustomView依赖注入(在活动范围内)

时间:2017-06-30 10:20:19

标签: android dependency-injection dagger-2 dagger

我的问题类似于this

例如,我有一个LiveData实现:

public class CustomLiveData extends LiveData<SomeEvent> {

    @Inject
    public CustomLiveData(@ActivityContext Context context) {
        //....
    }

}

我想要注入自定义视图:

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);
   }
}

以下是DI课程的其余部分:

@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{
}

请注意,如果将CustomLiveData注入MainActivity而不是注入视图,我就不会收到任何编译器投诉。 谢谢!

2 个答案:

答案 0 :(得分:27)

tl; dr不要在自定义View对象

中注入模型层依赖项

View的子类不是Dagger 2注射的良好目标。 View对象是用来绘制的,而不是其他对象,因此称为“视图”。 View的构造函数应该明确这一点;它们用于从XML中指定的属性中扩展View个对象。换句话说,View对象应该能够在layout.xml文件中指定,在生命周期的适当位置膨胀,然后使用findViewById(int id)Butterknife获取或数据绑定。通过这种方式,最佳自定义View对象不会依赖。

如果要链接View和模型层中的某些数据,标准模式是编写适用于RecyclerViewListView的适配器。如果无法做到这一点,那么使用setter(例如setData())比从构造函数中的模型层传递依赖项或从View的一个生命周期方法中请求注入更可取。

如果您使用LiveData类在活动或片段中注入AndroidInjector对象,则无需执行任何操作即可提供正确的Context。这解释了您的评论“如果将CustomLiveData注入到MainActivity而不是视图中,我不会收到任何编译器投诉。”

LiveData对象注入活动后,请使用上述方法之一(适配器或设置器)将数据与自定义View相关联。请参阅Google Android架构示例here,其中使用Dagger 2注入模型图层中的元素,然后使用ListViewfindViewById

setAdapter()相关联

链接到Dagger 2问题,其中讨论了View对象的注入:

https://github.com/google/dagger/issues/720

答案 1 :(得分:1)

您的Dagger层次结构如下所示: appcomponent - &gt; activitycomponent

您尝试在视图中注入活动context,这取决于appcomponent。{/ p>

这是不可能的,因为没有方法可以在appcomponent中提供活动上下文。相反,在内部视图中,您应该检索活动(例如使用getContext),从中提取activitycomponent,然后只注入CustomLiveData