Currently I'm using Dagger 2 to inject an instance of Retrofit to use for an api call in a widget. From my understanding, Dagger searches for things to inject using the type, so declaring 2 seperate @Provides Retrofit providesRetrofit()
with different names wouldn't work.
Heres my current code:
Module:
@Module
public class ApiModule {
@Provides
@Singleton
GsonConverterFactory provideGson() {
return GsonConverterFactory.create();
}
@Provides
@Singleton
RxJavaCallAdapterFactory provideRxCallAdapter() {
return RxJavaCallAdapterFactory.create();
}
@Singleton
@Provides
Retrofit providePictureRetrofit(GsonConverterFactory gsonConverterFactory, RxJavaCallAdapterFactory rxJavaCallAdapterFactory) {
Retrofit retrofit = new Retrofit.Builder()
.baseUrl(MarsWeatherWidget.PICTURE_URL)
.addConverterFactory(gsonConverterFactory)
.addCallAdapterFactory(rxJavaCallAdapterFactory)
.build();
return retrofit;
}
....
//Here is the other Retrofit instance where I was wanting to use a different URL.
// @Singleton
// @Provides
// Retrofit provideWeatherRetrofit(GsonConverterFactory gsonConverterFactory, RxJavaCallAdapterFactory rxJavaCallAdapterFactory) {
// Retrofit retrofit = new Retrofit.Builder()
// .baseUrl(MarsWeatherWidget.WEATHER_URL)
// .addConverterFactory(gsonConverterFactory)
// .addCallAdapterFactory(rxJavaCallAdapterFactory)
// .build();
// return retrofit;
// }
}
Component:
@Singleton
@Component(modules = ApiModule.class)
public interface ApiComponent {
void inject (MarsWeatherWidget marsWeatherWidget);
}
class extending Application
:
public class MyWidget extends Application {
ApiComponent mApiComponent;
@Override
public void onCreate() {
super.onCreate();
mApiComponent = DaggerApiComponent.builder().apiModule(new ApiModule()).build();
}
public ApiComponent getApiComponent() {
return mApiComponent;
}
}
and finally where im actually injecting it:
@Inject Retrofit pictureRetrofit;
@Override
public void onUpdate(Context context, AppWidgetManager appWidgetManager, int[] appWidgetIds) {
// There may be multiple widgets active, so update all of them
mAppWidgetIds = appWidgetIds;
((MyWidget) context.getApplicationContext()).getApiComponent().inject(this);
final int N = appWidgetIds.length;
for (int i = 0; i < N; i++) {
updateAppWidget(context, appWidgetManager, appWidgetIds[i]);
}
}
......
//use the injected Retrofit instance to make a call
So how can I organize this to give me a seperate Retrofit instance that is built with different URLs for hitting different APIs? Let me know if more info is needed.
You can use @Named
(or custom annotations that are annotated with @Qualifier
) to distinguish between variants of the same type.
Add the annotations like the following:
@Singleton
@Provides
@Named("picture")
Retrofit providePictureRetrofit(GsonConverterFactory gsonConverterFactory, RxJavaCallAdapterFactory rxJavaCallAdapterFactory) {
return retrofit = new Retrofit.Builder()
.baseUrl(MarsWeatherWidget.PICTURE_URL) // one url
.build();
}
@Singleton
@Provides
@Named("weather")
Retrofit provideWeatherRetrofit(GsonConverterFactory gsonConverterFactory, RxJavaCallAdapterFactory rxJavaCallAdapterFactory) {
return retrofit = new Retrofit.Builder()
.baseUrl(MarsWeatherWidget.WEATHER_URL) // other url
.build();
}
@Qualifier
You could also just create a custom annotation like the following:
@Qualifier
@Retention(RUNTIME)
public @interface Picture {}
You would just use this instead of @Named(String)
.
When you have your module providing the qualified types, you just need to also add the qualifier where you need the dependency.
MyPictureService provideService(@Named("picture") Retrofit retrofit) {
// ...
}
You should use a qualifier annotation to distinguish between different objects that have the same type—like between the Retrofit
for pictures and the Retrofit
for weather.
You apply the same qualifier to the @Provides
method and to the @Inject
parameter (constructor or method parameter, or field).
@Named
is one qualifier annotation, but using it means you have to remember to use the exact same string at the provision point and at all injection points. (It's easy to mistype @Named("whether")
somewhere.)
But it's easy to define your own qualifier annotation. Just define a custom annotation type, and annotate that with @Qualifier
:
@Documented
@Qualifier
public @interface Picture {}
@Documented
@Qualifier
public @interface Weather {}
Then you can bind each Retrofit
differently:
@Provides @Picture Retrofit providePictureRetrofit(…) {…}
@Provides @Weather Retrofit provideWeatherRetrofit(…) {…}
and inject each where you need it:
@Inject @Picture Retrofit pictureRetrofit;
@Inject @Weather Retrofit weatherRetrofit;
// (But constructor injection is better than field injection!)
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With