Sinyuk
Sinyuk

Reputation: 287

Retrofit cannot be provided without an @Inject constructor or from an @Provides- or @Produces-annotated method

I am learning Dagger 2 now and it's such a pain for me to explain the question without codes , so let me list all my modules, components and etc first :


App.class

public class App extends Application {

private ApiComponent mApiComponent = null;
private AppComponent mAppComponent = null;

public ApiComponent getApiComponent() {
    if (mApiComponent == null) {
        // Dagger%COMPONENT_NAME%
        mApiComponent = DaggerApiComponent.builder()
                // list of modules that are part of this component need to be created here too
                .appModule(new AppModule(this)) // This also corresponds to the name of your module: %component_name%Module
                .apiModule(new ApiModule(this))
                .build();

    }
    return mApiComponent;
}

public AppComponent getAppComponent() {
    if (mAppComponent == null) {
        // If a Dagger 2 component does not have any constructor arguments for any of its modules,
        // then we can use .create() as a shortcut instead:
        mAppComponent = DaggerAppComponent.builder()
                .appModule(new AppModule(this))
                .build();

    }
    return mAppComponent;
}
}

AppComponent

@Singleton
@Component(modules = AppModule.class)
public interface AppComponent {
    void inject(RetrofitDemo target);
}

AppModule

    private final Application mContext;

AppModule(Application context) {
    mContext = context;
}

@Singleton
@ForApplication
@Provides
Application provideApplication() {
    return mContext;
}

@Singleton
@ForApplication
@Provides
Context provideContext() {
    return mContext;
}

ApiComponent

@Singleton
@Component(dependencies = {AppModule.class},modules =    {ApiModule.class})
public interface ApiComponent {
     void inject(RetrofitDemo target);
}

APIModule

@Inject
Context application;

@Inject
public ApiModule(Context context){
    this.application = context;
}

@Provides
@Singleton
Gson provideGson() {
    return new GsonBuilder()
            // All timestamps are returned in ISO 8601 format:
            .setDateFormat("yyyy-MM-dd'T'HH:mm:ss'Z'")
            // Blank fields are included as null instead of being omitted.
            .serializeNulls()
            .create();
}


@Provides
@Singleton
OkHttpClient provideOkHttpClient() {
  ...
}



@Provides
@Singleton
public Retrofit provideRetrofit(Gson gson,OkHttpClient okHttpClient){
  return new Retrofit.Builder()
          .baseUrl(DribbleApi.END_POINT)
          .addConverterFactory(GsonConverterFactory.create(gson))
          .addCallAdapterFactory(RxJavaCallAdapterFactory.create())
          .client(okHttpClient)
          .build();
}

And my activity will be like this:

    @Inject
Retrofit mRetrofit;

@Override
protected void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    setContentView(R.layout.activity_retrofit_demo);
    ((App) getApplication()).getApiComponent().inject(this);
...

Here is the error message:

Error:(18, 10) : retrofit2.Retrofit cannot be provided without an @Inject constructor or from an @Provides- or @Produces-annotated method.
retrofit2.Retrofit is injected at com.sinyuk.yuk.RetrofitDemo.mRetrofit
com.sinyuk.yuk.RetrofitDemo is injected at com.sinyuk.yuk.AppComponent.inject(target)

What makes me confused is the retrofit instance is provided by the ApiModule, but why the error massage said it's injected at appComponent?And I can't find any place wrong in my code. T_T,it's too heavy going to learn Dagger for me...I think.


Besides, in my case I wrote dependencies = AppModule.class module = ApiModule.class in the AppComponent , and it seems to be right I think,but if I wrote module = ({AppComponent.class,ApiComponent.class}),it also works fine.Anybody can explain me why?


Kindly review my code and give me some advice. Thx in advance!

Upvotes: 5

Views: 5832

Answers (2)

EpicPandaForce
EpicPandaForce

Reputation: 81539

Okay so your configuration should be this

public class App extends Application {
    private AppComponent mAppComponent = null;

    public AppComponent getAppComponent() {
        if (mAppComponent == null) {
            // If a Dagger 2 component does not have any constructor arguments for any of its modules,
            // then we can use .create() as a shortcut instead:
            mAppComponent = DaggerAppComponent.builder()
                .appModule(new AppModule(this))
                .build();

        }
        return mAppComponent;
    }
}

And

@Singleton
@Component(modules = {AppModule.class, ApiModule.class})
public interface AppComponent {
    void inject(RetrofitDemo target);
}

And

@Module
public class AppModule {
    private final Application mContext;

    AppModule(Application context) {
        mContext = context;
    }

    @Provides
    Application provideApplication() {
        return mContext;
    }

    @Provides
    Context provideContext() {
        return mContext;
    }
}

And

@Module
public class ApiModule {
    @Provides
    @Singleton
    Gson provideGson() {
          return new GsonBuilder()
            // All timestamps are returned in ISO 8601 format:
            .setDateFormat("yyyy-MM-dd'T'HH:mm:ss'Z'")
            // Blank fields are included as null instead of being omitted.
            .serializeNulls()
            .create();
    }


    @Provides
    @Singleton
    OkHttpClient provideOkHttpClient() {
          ...
    }



    @Provides
    @Singleton
    public Retrofit provideRetrofit(Gson gson, OkHttpClient okHttpClient){
      return new Retrofit.Builder()
         .baseUrl(DribbleApi.END_POINT)
         .addConverterFactory(GsonConverterFactory.create(gson))
         .addCallAdapterFactory(RxJavaCallAdapterFactory.create())
         .client(okHttpClient)
         .build();
    }
}

And

//...Activity

@Inject
Retrofit mRetrofit;

@Override
protected void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    setContentView(R.layout.activity_retrofit_demo);
    ((App) getApplication()).getAppComponent().inject(this);
...

Upvotes: 1

OYRM
OYRM

Reputation: 1415

@Sinyuk There's a lot to unpack here, and Dagger is a little complicated at first blush, but I think I can help. First, you have a conceptual misunderstanding regarding the @Component annotation. A Component is an interface which you define, and which Dagger implements through code generation. You will define the interface, and annotate it with @Component and then you will provide a set of Modules to Dagger to use in the generation process. The modules which you use are passed in through the modules element of the @Component annotation. If you want to have one Component permit another Component to support the injection process, then any Component interfaces which you need to have Dagger use while injecting your code, will be passed in through the dependencies element of the @Component annotation.

--

As a result, The following is incorrect

@Component(dependencies = AppModule.class  module = ApiModule.class`)

instead, to have the one Component use two Modules write:

@Component(modules = {ApiModule.class, AppModule.class})

or, to have one Component use one Module and depend upon the other Component

@Component(modules = {AppModule.class}, dependencies = {ApiComponent.class})

I hope that that helps you get onto the right path. Let me know if you have any follow up questions.

Upvotes: 5

Related Questions