Подтвердить что ты не робот

Ошибка кинжала 2: зависимость "не может быть предоставлена ​​без конструктора @Inject", в то время как она фактически аннотируется с помощью @Inject

Я начал использовать Dagger 2 и столкнулся с странной проблемой, которая выглядит как ошибка для меня.

У меня есть 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;
    }
}

Второй модуль выглядит следующим образом: он предоставляет презентатор/контроллер и их зависимости для подмножества пользовательского интерфейса на экране:

@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-annotated, но он существует! Если я заменю неявное создание экземпляра NavigationListControllerImpl (передавая через параметр @Provides -method) с явным (с new), кинжал начинает жаловаться на ту же ошибку, но теперь для объекта-презентатора, который является второй записью в том же модуле, и и так далее.

Вся эта ситуация выглядит очень странно, и я хотел бы услышать некоторые материалы от более опытных пользователей Dagger 2 (и разработчиков?).

Заранее благодарю вас!

4b9b3361

Ответ 1

Похоже, я выяснил, что случилось с настройкой Dagger 2. Невозможно использовать ту же область как в компоненте, так и подкомпонентах. Он потребовал определить новую область для подкомпонента. В моем случае я закончил создание области @Screen для подкомпонента.

Я бы сказал, что это небольшой, но очень раздражающий дефект в кинжале 2. Очевидно, компилятор кинжалов сообщает о приятной и понятной ошибке о тех же областях в родительском компоненте и дочернем компоненте, если дочерний компонент расширен с родительским компонентом как зависимость. Но полностью вводящая в заблуждение ошибка сообщается компилятором, если родительский компонент и дочерний подкомпонент имеют одну и ту же область.

Спасибо, @lukas, за подсказку здесь fooobar.com/questions/202949/..., которая привела к разрешению проблемы.

Ответ 2

У меня такая же ошибка, потому что я забыл разоблачить объекты, предоставленные модулями в родительском компоненте, другим компонентам, зависящим от него.

Пример родительского компонента:

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

Обновление:

AppModule:

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

Ответ 3

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

Ответ 4

Пришел к этому вопросу сегодня. Для меня возникла проблема с обработкой аннотации (на Android Studio 2.2 с gradle 2.x).

Вместо ~~ apt ~~ я использовал annotationProcessor Я использовал

annotationProcessor 'com.google.dagger:dagger-compiler:2.6'

и теперь он работает.

Ответ 5

Произошла одна и та же проблема при попытке создания подкомпонентов, но, похоже, она исправлена ​​в Dagger 2.0.1.

Ответ 6

Похоже, что это то же самое, что и в случае с кинжалом, для многих ошибок. В моем случае моя целевая инъекция ожидала конкретного класса (Presenter), где в качестве модуля, предоставляющего презентатору, возвращался только интерфейс (DemoContract.Presenter)

Так что изменилось с

@Inject
public Presenter mDemoPresenter;  

до

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