Dagger 2错误:依赖项“无法提供没有@ Inject构造函数”的同时实际上已用@ Inject注释。

50

我开始使用Dagger 2,并遇到一个奇怪的问题,看起来像是一个bug。

我有3个模块,它们组成了一个子组件,该子组件又扩展/增加了更高级别的组件。

子组件非常简单:只是模块的组合和一个单一的注入点:

@Singleton
@Subcomponent(
        modules = {
                NavigationDrawerModule.class,
                NavigationListModule.class,
                SwitcherModule.class
        }
)
public interface NavigationDrawerComponent {


    NavigationDrawerFragment inject(NavigationDrawerFragment object);

}

第一个模块看起来像这样-它提供一般的片段级别依赖关系:

@Module
public class NavigationDrawerModule {

    private final Activity activity;
    private final View rootView;
    private final LoaderManager loaderManager;

    public NavigationDrawerModule(Activity activity, View rootView, LoaderManager loaderManager) {
        this.activity = activity;
        this.rootView = rootView;
        this.loaderManager = loaderManager;
    }

    @Provides @Singleton EventBus provideLocalBus() {
        return EventBus.builder().build();
    }

    @Provides @Singleton View provideViewRoot() {
        return rootView;
    }

    @Provides @Singleton LoaderManager provideLoaderManager() {
        return loaderManager;
    }

    @Provides @Singleton Context provideContext() {
        return activity;
    }
}

第二个模块看起来像这样 - 它为屏幕上的一部分UI提供了展示器/控制器及其依赖项:

@Module
public class SwitcherModule {

    @Provides SwitchController provideSwitcherController(SwitchControllerImpl impl) {
        return impl;
    }

    @Provides SwitcherView provideSwitcherView(SwitcherViewImpl impl) {
        return impl;
    }

}

第三个模块 - 为UI的一个子集提供另一个展示者/控制器:

@Module
public class NavigationListModule {

    @Provides @Singleton NavigationListController provideNavigationListController(NavigationListControllerImpl impl) {
        return impl;
    }

    @Provides @Singleton NavigationListView provideNavigationListView(NavigationListViewImpl impl) {
        return impl;
    }
}

被注入的片段中相关的部分:

@Inject SwitchController identitySwitchController;
@Inject SwitcherView identitySwitcherView;
@Inject NavigationListController navigationListController;
@Inject NavigationListView navigationListView;

NavigationListControllerImpl 实现了以下构造函数:

@Inject
public NavigationListControllerImpl(Context ctx, EventBus bus) {
    this.ctx = ctx;
    this.bus = bus;
}

我从Dagger 2编译器收到的错误信息如下:

error: ...sidenavigation.navigationlist.NavigationListControllerImpl cannot be provided without an @Inject constructor or from an @Provides-annotated method.
...sidenavigation.NavigationDrawerFragment.navigationListController
[injected field of type: ...sidenavigation.navigationlist.NavigationListController navigationListController]
...sidenavigation.navigationlist.NavigationListModule.provideNavigationListController(...sidenavigation.navigationlist.NavigationListControllerImpl impl)
[parameter: ...sidenavigation.navigationlist.NavigationListControllerImpl impl]

错误提示缺少带有@Inject注解的构造函数,但实际上已经存在!如果我用显式创建NavigationListControllerImpl实例(使用@Provides方法参数传递)替换隐式创建,那么dagger就会开始报同样的错误,但这次是针对同一模块中的第二个presenter对象,以此类推。

所有这些情况看起来非常奇怪,我想听听更有经验的Dagger 2用户(和开发者)的看法。

提前感谢您!


如果您还没有解决这个问题,能否确认一下 @Inject 注释是否为 javax.inject.Inject? - Jeff Bowman
这个问题仍然困扰着我。是的,我确认 @Inject 实际上是 javax.inject.Inject。全局组件在我添加子组件之前运行良好 - 然后我就遇到了错误。 - dominus
2
我在Dagger 2的Github页面上发布了问题。其中包含更多细节 https://github.com/google/dagger/issues/188 - dominus
6个回答

58
我因为忘记将父组件提供的模块对象暴露给依赖它的其他组件而遇到了相同的错误。
父组件示例:
@Singleton
@Component(modules = {AppModule.class})
public interface AppComponent {
    AppPref exposeAppPref(); /* my issue was caused by forgot this line,
the method name doesn't matter, what matters is the object type AppPref provided in the AppModule 
that you want it to be available in the component that declares this component as one of its dependencies*/
}

以下是将上述组件作为依赖项的示例组件

@UserScope
@Component (dependencies = {AppComponent.class})
public interface ActivityComponent {
    void inject(MainActivity activity);
}

Update:

AppModule:

...
    @Provides
    @Singleton
    AppPref provideAppPref() {
        return appPref;
    }
...

19
更多证据表明Dagger很糟糕。 - Johann

20

GlobalComponent和子组件NavigationDrawerComponent必须具有不同的作用域。将@Singleton用于GlobalComponent,并将其他作用域用于子组件。

否则,如果将相同的作用域应用于GlobalComponent和子组件,则必须在全局组件中声明子组件的模块:

@Component(
        // modules from subcomponent must be declared here also
        modules = {NavigationListModule.class, 
                  SwitcherModule.class, 
                  NavigationDrawerModule.class,
                  ...}
)
@Singleton
public interface GlobalComponent {
   NavigationDrawerComponent plus(NavigationDrawerModule module);
}

对于您的使用情况,您也可以使用组件依赖项。例如:

@Component(
        dependencies = GlobalComponent.class,
        modules = {NavigationListModule.class, 
                  SwitcherModule.class, 
                  NavigationDrawerModule.class}
)
@YourOtherDaggerScope // @Singleton scope won't work here, it must be a different scope
public interface NavigationDrawerComponent extends GlobalComponent { // extend the parent component if you wish to get access to parent dependencies

   NavigationDrawerFragment inject(NavigationDrawerFragment object);
}

2
请问能否指引我到相关文档的摘录部分?问题在于这样的修复会破坏子组件的存在意义——任何依赖都将全局可用,可能会意外地依赖于某个短暂屏幕上的本地内容。 - dominus
我添加了一个编辑,以显示我更愿意在这里使用组件依赖项。关于子组件及其模块声明,文档并没有清楚地说明,但就我对组件和子组件之间的紧密耦合的理解而言,它应该是这样的。 - lukasz
感谢您在示例代码中提供的提示。关键问题在于我在父组件和子@Subcomponent中都使用了相同的@Singleton作用域。 - dominus
你说得对!我编辑了我的答案以反映你的解决方案,以便其他可能阅读它的人更清楚明白。 - lukasz
这是错误的。 你说得对,但你做错了;“GlobalComponent和子组件NavigationDrawerComponent必须具有不同的作用域”- 如果您在两个不同作用域的组件中使用两次相同的模块(如您的代码示例中所示),那么Dagger编译器将会抛出错误。 - murt

17

看起来我已经弄清楚了我的Dagger 2设置出了什么问题。无法在组件和子组件中使用相同的作用域,必须为子组件定义新的作用域。在我的情况下,我最终为子组件创建了 @Screen 作用域。

我想说,这是Dagger 2中一个很小但非常烦人的缺陷。显然,如果将父组件作为依赖项扩展到子组件上,则dagger-compiler会报告关于父组件和子组件中相同作用域的错误,并且错误消息容易理解。但是,如果父组件和子组件共享相同的作用域,则编译器会报告完全误导性的错误。

感谢@lukas在https://dev59.com/fV0a5IYBdhLWcg3wfYw6#30383088给我指引,帮助我解决了这个问题。


如果Dagger 2.13在使用不同的嵌套作用域时出现问题,如何使用子组件? - Sagar Nayak
@SagarNayak,你解决了这个问题吗? - abhishek

5
今天我也遇到了这个问题。对我来说,注释处理出了问题(在使用gradle 2.x的Android Studio 2.2上)。
我使用了annotationProcessor而不是apt 我使用了 annotationProcessor 'com.google.dagger:dagger-compiler:2.6' 现在它可以工作了。

这终于对我起作用了!如果您正在使用最新版本的Android Studio,即2.2.2,您需要按照Lord Flash的答案使用annotationProcessor - Subby
当我使用annotationProcessor时,我遇到了这个错误 - Error:(53,30)错误:找不到符号变量DaggerActivityComponent而且 当我使用apt时,我遇到了这个错误 - 不能在没有@Inject构造函数或从中提供。请告诉我现在该怎么办? - Shoeb Siddique

2

在尝试创建子组件时遇到了相同的问题,但似乎在Dagger 2.0.1中已经修复了。


2

看起来这是dagger报告的很多错误类型。在我的情况下,我的目标注入期望具体类(Presenter),而提供Presenter的模块仅返回接口(DemoContract.Presenter)

所以从以下内容进行更改:

@Inject
public Presenter mDemoPresenter;  

to

@Inject
public DemoContract.Presenter mDemoPresenter;

提供演示者外观的模块如下:

@Module
public class DiDemoPresenterModule {
    private final DemoContract.View mView;

    DiDemoPresenterModule(MainActivity mView) {
        this.mView = mView;
    }

    @Provides
    public DemoContract.Presenter providesDemoPresenter(Repository repository) {
        return new DemoPresenter(repository, mView);
    }
}

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接