Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Dagger 2 cannot be provided without an @Provides-annotated method

I know there are many similar questions but I still haven't been able to find a solution for my problem and at this stage, I'm out of ideas. I have the following setup:

  • Application module/component: just for the context and the Application object.
  • Net module/component: retrofit client
  • City module/component: a module/component to inject dependencies in a MVP screen. I want to inject the Presenter and the Interactor in the Fragment.
  • PlaceRequests: a retrofit interface

This is how the code looks:

ApplicationModule.java

@Module
public class ApplicationModule {
    private Application mApp;

    public ApplicationModule(Application app) {
        mApp = app;
    }

    @Provides
    @Singleton
    public Application provideApplication() {
        return mApp;
    }

    @Provides
    @Singleton
    Context provideApplicationContext() {
        return mApp.getApplicationContext();
    }
}

ApplicationComponent.java

@Singleton
@Component(modules = {ApplicationModule.class})
public interface ApplicationComponent {
    Application application();
    Context getContext();
}

NetModule.java

@Module
public class NetModule {

    String mBaseUrl;

    public NetModule(String baseUrl) {
        this.mBaseUrl = baseUrl;
    }

    @Provides
    @Singleton
    SharedPreferences providesSharedPreferences(Application application) {
        return PreferenceManager.getDefaultSharedPreferences(application);
    }

    @Provides
    @Singleton
    Cache provideOkHttpCache(Application application) {
        int cacheSize = 10 * 1024 * 1024; // 10 MiB
        return new Cache(application.getCacheDir(), cacheSize);
    }

    @Provides
    @Singleton
    OkHttpClient provideOkHttpClient(Cache cache) {
        OkHttpClient client = new OkHttpClient();
        client.setCache(cache);
        return client;
    }

    @Provides
    @Singleton
    Retrofit provideRetrofit(OkHttpClient okHttpClient) {
        return new Retrofit.Builder()
                .addConverterFactory(JacksonConverterFactory.create())
                .addCallAdapterFactory(RxJavaCallAdapterFactory.create())
                .baseUrl(mBaseUrl)
                .client(okHttpClient)
                .build();
    }

    @Provides
    @Singleton
    PlaceRequests providePlaceRequests(Retrofit retrofit) {
        return retrofit.create(PlaceRequests.class);
    }
}

NetComponent.java

@Singleton
@Component(
        dependencies = {ApplicationModule.class},
        modules = {NetModule.class}
)
public interface NetComponent {
    Application application();
    PlaceRequests getPlaceRequests();
}

CityModule.java

@Module
public class CityModule {

    private CityMvp.View view;

    public CityModule(CityMvp.View view) {
        this.view = view;
    }

    @Provides
    public CityMvp.View provideView() {
        return view;
    }

    @Provides
    public CityMvp.Interactor provideInteractor(PlaceRequests placeRequests) {
        return new CityInteractor(placeRequests);
    }

    @Provides
    public CityPresenter providePresenter(CityMvp.View cityView, CityMvp.Interactor interactor) {
        return new CityPresenter(cityView, interactor);
    }
}

CityComponent.java

@PerFragment
@Component(
        dependencies = {NetModule.class},
        modules = {CityModule.class}
)

public interface CityComponent {
    void inject(CityFragment cityFragment);
}

CityInteractor.java (the one that is not able to inject cause the PlaceRequests dependency)

public class CityInteractor implements CityMvp.Interactor {

    private PlaceRequests placeRequests;

    public CityInteractor(PlaceRequests placeRequests) {
        this.placeRequests = placeRequests;
    }

    @Override
    public void getPlaceDetails(String placeId, String key, Subscriber<PlaceDetails> subscriber) {
        Observable<PlaceDetails> observable = placeRequests.getPlaceDetails(placeId, key);
        observable.observeOn(AndroidSchedulers.mainThread())
                .subscribeOn(Schedulers.io())
                .subscribe(subscriber);
    }
}

Finally, the error trace:

    : error: com.blabla.blabla.webapi.place.PlaceRequests cannot be provided without an @Provides-annotated method.
    void inject(CityFragment cityFragment);
         ^
      com.blabla.blabla.screens.city.CityFragment.presenter
          [injected field of type: com.blabla.blabla.screens.city.CityPresenter presenter]
      com.blabla.blabla.di.modules.CityModule.providePresenter(com.blabla.blabla.screens.city.CityMvp.View cityView, com.blabla.blabla.screens.city.CityMvp.Interactor interactor)
          [parameter: com.blabla.blabla.screens.city.CityMvp.Interactor interactor]
      com.blabla.blabla.di.modules.CityModule.provideInteractor(com.blabla.blabla.webapi.place.PlaceRequests placeRequests)
          [parameter: com.blabla.blabla.webapi.place.PlaceRequests placeRequests]
3 errors
:app:compileDebugJavaWithJavac FAILED

FAILURE: Build failed with an exception.

From what I understand, I'm exposing PlaceRequests object in NetComponent.java, and NetModule.java is a dependency of CityComponent. How come I'm not able to get PlaceRequests dependency from CityComponent? What am I missing? Thanks!

like image 819
Rafag Avatar asked Jan 08 '17 14:01

Rafag


1 Answers

Component dependencies specified through @Component(dependencies={...}) shouldn't be Modules, as you have them. They should be types (typically Components) that make dependencies available through zero-arg methods known as provision methods.

Switch your dependencies to the components instead of modules.

If it helps, you might want to change how you think about Components, Modules, and component dependencies. You can think of Dagger as creating an object graph where Modules define the inputs or bindings of the graph and Components define the outputs or consumers of the graph. This leaves component dependencies to be types or components included or imported from another external source, which might include a different Dagger-created Component, but probably wouldn't include a Module--instead of a dependency on the Module you'd depend on the Component that consumes the module.

like image 125
Jeff Bowman Avatar answered Nov 19 '22 15:11

Jeff Bowman