Reputation: 287
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
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
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 Module
s 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