I create dagger2 module for retrofit2
#Module
public class NetworkModule {
private Context context;
public NetworkModule(Application app) {
this.context = app;
}
#Singleton
#Provides
Context providesContext() {
return context;
}
#Singleton
#Provides
OkHttpClient providesOkHttpClient(Utils utils) {
User user = utils.getSettings();
return new OkHttpClient.Builder()
.connectTimeout(Long.valueOf(user.getTimeOut()), TimeUnit.SECONDS)
.writeTimeout(Long.valueOf(user.getTimeOut()), TimeUnit.SECONDS)
.readTimeout(Long.valueOf(user.getTimeOut()), TimeUnit.SECONDS)
.build();
}
#Singleton
#Provides
Retrofit providesRetrofit(OkHttpClient okHttpClient, Utils utils) {
User user = utils.getSettings();
String host = user.getHost();
if (!host.endsWith("/")) host += "/";
return new Retrofit.Builder()
.baseUrl(host)
.addConverterFactory(JacksonConverterFactory.create())
.addCallAdapterFactory(RxJava2CallAdapterFactory.create())
.client(okHttpClient).build();
}
#Singleton
#Provides
RestApiFactory providesRestApiFactory(Retrofit retrofit) {
return new RestApiFactory(retrofit);
}
}
I have settings activity where user can change baseUrl, timeOut. If I create
providesRetrofit
providesRestApiFactory
providesOkHttpClient
like #Singleton - after change settings it not change. If I remove #Singleton annotation - all work. My questions - how can I update dagger #Singleton when user change data?
Place NetworkModule in separate component(likely Subcomponent of your main Component) and recreate it when user changes baseUrl, timeout etc.
You can do that by creating interface annotated with #Subcomponent and adding your network module there. You can instantiate this Subcomponent by invoking method from your main component that you also need to add.
Detailed tutorial
https://google.github.io/dagger/subcomponents.html
Related
I am using Dagger2 in my app to provide dependencies. I get this following error when I build my app.
e: /Users/sriramr/Desktop/android/Movie/MovieInfo/app/build/generated/source/kapt/debug/in/sriram/movieinfo/di/ActivityBuilder_BindMoviesListActivity.java:22: error: in.sriram.movieinfo.di.ActivityBuilder_BindMoviesListActivity.MoviesListActivitySubcomponent (unscoped) may not reference scoped bindings:
#Subcomponent(modules = MoviesListActivityModule.class)
^
#Provides #Singleton in.sriram.movieinfo.network.TmdbService in.sriram.movieinfo.di.MoviesListActivityModule.getTmdbService(retrofit2.Retrofit)
#Provides #Singleton retrofit2.Retrofit in.sriram.movieinfo.di.NetworkModule.getRetrofit(okhttp3.OkHttpClient, retrofit2.converter.gson.GsonConverterFactory, retrofit2.adapter.rxjava2.RxJava2CallAdapterFactory)
#Provides #Singleton okhttp3.OkHttpClient in.sriram.movieinfo.di.NetworkModule.getOkHttpClient(okhttp3.logging.HttpLoggingInterceptor, okhttp3.Cache)
#Provides #Singleton okhttp3.logging.HttpLoggingInterceptor in.sriram.movieinfo.di.NetworkModule.getHttpLoggingInterceptor()
#Provides #Singleton okhttp3.Cache in.sriram.movieinfo.di.NetworkModule.getCacheFile(#Named("application-context") android.content.Context)
#Provides #Singleton retrofit2.converter.gson.GsonConverterFactory in.sriram.movieinfo.di.NetworkModule.getGsonConverterFactory()
#Provides #Singleton retrofit2.adapter.rxjava2.RxJava2CallAdapterFactory in.sriram.movieinfo.di.NetworkModule.getRxJavaFactory()
#Provides #Singleton in.sriram.movieinfo.cache.AppDatabase in.sriram.movieinfo.di.ContextModule.getAppDatabase(#Named("application-context") android.content.Context)
#Provides #Singleton com.squareup.picasso.Picasso in.sriram.movieinfo.di.MoviesListActivityModule.getPicasso(#Named("application-context") android.content.Context)
This is my ContextModule
#Module
public class ContextModule {
#Provides
#Named("application-context")
Context getContext(Application app) {
return app;
}
#Provides
#Singleton
AppDatabase getAppDatabase(#Named("application-context") Context context) {
return Room.databaseBuilder(context,
AppDatabase.class, "database-name").build();
}
}
And this is my NetworkModule
#Module(includes = ContextModule.class)
public class NetworkModule {
#Provides
#Singleton
Cache getCacheFile(#Named("application-context") Context context) {
File cacheFile = new File(context.getCacheDir(), "moviedb-cache");
return new Cache(cacheFile, 10 * 1000 * 1000);
}
#Provides
#Singleton
OkHttp3Downloader getOkHttp3Downloader(OkHttpClient okHttpClient) {
return new OkHttp3Downloader(okHttpClient);
}
#Provides
#Singleton
OkHttpClient getOkHttpClient(HttpLoggingInterceptor loggingInterceptor, Cache cache) {
return new OkHttpClient.Builder()
.addInterceptor(loggingInterceptor)
.cache(cache)
.build();
}
#Provides
#Singleton
Retrofit getRetrofit(OkHttpClient client, GsonConverterFactory gsonConverterFactory, RxJava2CallAdapterFactory callAdapter) {
return new Retrofit.Builder()
.addConverterFactory(gsonConverterFactory)
.addCallAdapterFactory(callAdapter)
.baseUrl("https://api.themoviedb.org/3/")
.client(client)
.build();
}
#Provides
#Singleton
HttpLoggingInterceptor getHttpLoggingInterceptor() {
HttpLoggingInterceptor loggingInterceptor = new HttpLoggingInterceptor(message -> Timber.tag("OkHttp").d(message));
loggingInterceptor.setLevel(HttpLoggingInterceptor.Level.BODY);
return loggingInterceptor;
}
#Provides
#Singleton
GsonConverterFactory getGsonConverterFactory() {
return GsonConverterFactory.create();
}
#Provides
#Singleton
RxJava2CallAdapterFactory getRxJavaFactory() {
return RxJava2CallAdapterFactory.create();
}
}
And finally the MovieListActivityModule
#Module(includes = NetworkModule.class)
public class MoviesListActivityModule {
#Provides
#Singleton
TmdbService getTmdbService(Retrofit retrofit) {
return retrofit.create(TmdbService.class);
}
#Provides
#Singleton
Picasso getPicasso(#Named("application-context") Context context) {
return new Picasso.Builder(context)
.loggingEnabled(true)
.build();
}
}
And this is the ActivityBuilder class
#Module
public abstract class ActivityBuilder {
#ContributesAndroidInjector(modules = MoviesListActivityModule.class)
abstract MoviesListActivity bindMoviesListActivity();
}
And finally the AppComponent
#Singleton
#Component(modules = {AndroidInjectionModule.class, ContextModule.class, ActivityBuilder.class})
public interface AppComponent {
#Component.Builder
interface Builder {
#BindsInstance
Builder application(Application application);
AppComponent build();
}
void inject(MovieInfoApp app);
}
I am new to Dagger 2 and I followed this from a random tutorial of Medium.
I don't see any SubComponent here. I understand that the subcomponent is generated.
This error just occurs for the #Singleton scoped dependencies.
I followed some stack overflow links and added #Singleton to the AppComponent interface.
How do I fix this?
Rename MoviesListActivityModule to MoviesListApplicationModule, take it off of your #ContributesAndroidInjector, and put it onto your AppComponent's modules list instead.
Behind the scenes, #ContributesAndroidInjector generates a #Subcomponent specific to your MoviesListActivity. When you try to inject your MoviesListActivity using AndroidInjection.inject(Activity), Dagger creates a subcomponent instance that holds MoviesListActivity's MembersInjector (the generated code that knows how to populate all the #Inject fields in your MoviesListActivity) and any scoped bindings that your Activity (or its dependencies) may need. That's the component you're missing, and it's named after the Module and #ContributesAndroidInjector method name, ActivityBuilder_BindMoviesListActivity.MoviesListActivitySubcomponent.
You can add scopes (like an #ActivityScope you create) to #ContributesAndroidInjector, and dagger.android will add them onto the generated subcomponent, just like setting modules = {/*...*/} will add modules onto it. However, that isn't your problem.
First of all, you were right to add #Singleton to your AppComponent; that's a very common thing, and it's right to do here. Adding #Singleton there means that Dagger will automatically watch out for bindings that are marked with #Singleton and keep copies of them in the component you annotate the same way. If you were to create an #ActivityScope annotation and add it to your subcomponent, Dagger would watch out for bindings that were annotated with #ActivityScope and return the same instance from within the same Activity instance but a different instance compared to other instances of the same Activity or other Activity types.
Your problem is that you are adding #Singleton-scoped bindings Picasso and TmdbService into your unscoped subcomponent graph. What you're telling Dagger is that across the lifetime of your application component (not your Activity, your application) you should always return the same Picasso and TmdbService instances. However, by making that binding on the module list of your subcomponent rather than your top-level #Singleton #Component, you tell Dagger about these application-level objects when it's trying to configure activity-level bindings. The same applies to the bindings in your NetworkModule, which are also listed in your error message.
After you move the modules, you will always receive the same instance of Picasso, TmdbService, OkHttpClient, and all of those others—which should allow those objects to help manage caches, batches, and requests in flight without you having to worry about which instance you're interacting with. It'll always be the same instance across the lifetime of your app.
Hey there I am using Dagger2, Retrofit and OkHttp and I am facing dependency cycle issue.
When providing OkHttp :
#Provides
#ApplicationScope
OkHttpClient provideOkHttpClient(TokenAuthenticator auth,Dispatcher dispatcher){
return new OkHttpClient.Builder()
.connectTimeout(Constants.CONNECT_TIMEOUT, TimeUnit.SECONDS)
.readTimeout(Constants.READ_TIMEOUT,TimeUnit.SECONDS)
.writeTimeout(Constants.WRITE_TIMEOUT,TimeUnit.SECONDS)
.authenticator(auth)
.dispatcher(dispatcher)
.build();
}
When providing Retrofit :
#Provides
#ApplicationScope
Retrofit provideRetrofit(Resources resources,Gson gson, OkHttpClient okHttpClient){
return new Retrofit.Builder()
.baseUrl(resources.getString(R.string.base_api_url))
.addConverterFactory(GsonConverterFactory.create(gson))
.addCallAdapterFactory(RxJava2CallAdapterFactory.create())
.client(okHttpClient)
.build();
}
When providing APIService :
#Provides
#ApplicationScope
APIService provideAPI(Retrofit retrofit) {
return retrofit.create(APIService.class);
}
My APIService interface :
public interface APIService {
#FormUrlEncoded
#POST("token")
Observable<Response<UserTokenResponse>> refreshUserToken();
--- other methods like login, register ---
}
My TokenAuthenticator class :
#Inject
public TokenAuthenticator(APIService mApi,#NonNull ImmediateSchedulerProvider mSchedulerProvider) {
this.mApi= mApi;
this.mSchedulerProvider=mSchedulerProvider;
mDisposables=new CompositeDisposable();
}
#Override
public Request authenticate(Route route, Response response) throws IOException {
request = null;
mApi.refreshUserToken(...)
.subscribeOn(mSchedulerProvider.io())
.observeOn(mSchedulerProvider.ui())
.doOnSubscribe(d -> mDisposables.add(d))
.subscribe(tokenResponse -> {
if(tokenResponse.isSuccessful()) {
saveUserToken(tokenResponse.body());
request = response.request().newBuilder()
.header("Authorization", getUserAccessToken())
.build();
} else {
logoutUser();
}
},error -> {
},() -> {});
mDisposables.clear();
stop();
return request;
}
My logcat :
Error:(55, 16) error: Found a dependency cycle:
com.yasinkacmaz.myapp.service.APIService is injected at com.yasinkacmaz.myapp.darkvane.modules.NetworkModule.provideTokenAuthenticator(…, mApi, …)
com.yasinkacmaz.myapp.service.token.TokenAuthenticator is injected at
com.yasinkacmaz.myapp.darkvane.modules.NetworkModule.provideOkHttpClient(…, tokenAuthenticator, …)
okhttp3.OkHttpClient is injected at
com.yasinkacmaz.myapp.darkvane.modules.NetworkModule.provideRetrofit(…, okHttpClient)
retrofit2.Retrofit is injected at
com.yasinkacmaz.myapp.darkvane.modules.NetworkModule.provideAPI(retrofit)
com.yasinkacmaz.myapp.service.APIService is provided at
com.yasinkacmaz.myapp.darkvane.components.ApplicationComponent.exposeAPI()
So my question: My TokenAuthenticator class is depends on APIService but I need to provide TokenAuthenticator when creating APIService. This causes dependency cycle error. How do I beat this , is there anyone facing this issue ?
Thanks in advance.
Your problem is:
Your OKHttpClient depends on your Authenticator
Your Authenticator depends on a Retrofit Service
Retrofit depends on an OKHttpClient (as in point 1)
Hence the circular dependency.
One possible solution here is for your TokenAuthenticator to depend on an APIServiceHolder rather than a APIService. Then your TokenAuthenticator can be provided as a dependency when configuring OKHttpClient regardless of whether the APIService (further down the object graph) has been instantiated or not.
A very simple APIServiceHolder:
public class APIServiceHolder {
private APIService apiService;
#Nullable
APIService apiService() {
return apiService;
}
void setAPIService(APIService apiService) {
this.apiService = apiService;
}
}
Then refactor your TokenAuthenticator:
#Inject
public TokenAuthenticator(#NonNull APIServiceHolder apiServiceHolder, #NonNull ImmediateSchedulerProvider schedulerProvider) {
this.apiServiceHolder = apiServiceHolder;
this.schedulerProvider = schedulerProvider;
this.disposables = new CompositeDisposable();
}
#Override
public Request authenticate(Route route, Response response) throws IOException {
if (apiServiceHolder.get() == null) {
//we cannot answer the challenge as no token service is available
return null //as per contract of Retrofit Authenticator interface for when unable to contest a challenge
}
request = null;
TokenResponse tokenResponse = apiServiceHolder.get().blockingGet()
if (tokenResponse.isSuccessful()) {
saveUserToken(tokenResponse.body());
request = response.request().newBuilder()
.header("Authorization", getUserAccessToken())
.build();
} else {
logoutUser();
}
return request;
}
Note that the code to retrieve the token should be synchronous. This is part of the contract of Authenticator. The code inside the Authenticator will run off the main thread.
Of course you will need to write the #Provides methods for the same:
#Provides
#ApplicationScope
apiServiceHolder() {
return new APIServiceHolder();
}
And refactor the provider methods:
#Provides
#ApplicationScope
APIService provideAPI(Retrofit retrofit, APIServiceHolder apiServiceHolder) {
APIService apiService = retrofit.create(APIService.class);
apiServiceHolder.setAPIService(apiService);
return apiService;
}
Note that mutable global state is not usually a good idea. However, if you have your packages organised well you may be able to use access modifiers appropriately to avoid unintended usages of the holder.
Using the Lazy interface of Dagger 2 is the solution here.
In your TokenAuthenticator replace APIService mApi with Lazy<APIService> mApiLazyWrapper
#Inject
public TokenAuthenticator(Lazy<APIService> mApiLazyWrapper,#NonNull ImmediateSchedulerProvider mSchedulerProvider) {
this.mApiLazyWrapper= mApiLazyWrapper;
this.mSchedulerProvider=mSchedulerProvider;
mDisposables=new CompositeDisposable();
}
And to get the APIService instance from wrapper use mApiLazyWrapper.get()
In case mApiLazyWrapper.get() returns null, return null from the authenticate method of TokenAuthenticator as well.
Big thanks to #Selvin and #David. I have two approach, one of them is David's answer and the other one is :
Creating another OkHttp or Retrofit or another library which will handle our operations inside TokenAuthenticator class.
If you want to use another OkHttp or Retrofit instance you must use Qualifier annotation.
For example :
#Qualifier
public #interface ApiClient {}
#Qualifier
public #interface RefreshTokenClient {}
then provide :
#Provides
#ApplicationScope
#ApiClient
OkHttpClient provideOkHttpClientForApi(TokenAuthenticator tokenAuthenticator, TokenInterceptor tokenInterceptor, Dispatcher dispatcher){
return new OkHttpClient.Builder()
.connectTimeout(Constants.CONNECT_TIMEOUT, TimeUnit.SECONDS)
.readTimeout(Constants.READ_TIMEOUT,TimeUnit.SECONDS)
.writeTimeout(Constants.WRITE_TIMEOUT,TimeUnit.SECONDS)
.authenticator(tokenAuthenticator)
.addInterceptor(tokenInterceptor)
.dispatcher(dispatcher)
.build();
}
#Provides
#ApplicationScope
#RefreshTokenClient
OkHttpClient provideOkHttpClientForRefreshToken(Dispatcher dispatcher){
return new OkHttpClient.Builder()
.connectTimeout(Constants.CONNECT_TIMEOUT, TimeUnit.SECONDS)
.readTimeout(Constants.READ_TIMEOUT,TimeUnit.SECONDS)
.writeTimeout(Constants.WRITE_TIMEOUT,TimeUnit.SECONDS)
.dispatcher(dispatcher)
.build();
}
#Provides
#ApplicationScope
#ApiClient
Retrofit provideRetrofitForApi(Resources resources, Gson gson,#ApiClient OkHttpClient okHttpClient){
return new Retrofit.Builder()
.baseUrl(resources.getString(R.string.base_api_url))
.addConverterFactory(GsonConverterFactory.create(gson))
.addCallAdapterFactory(RxJava2CallAdapterFactory.create())
.client(okHttpClient)
.build();
}
#Provides
#ApplicationScope
#RefreshTokenClient
Retrofit provideRetrofitForRefreshToken(Resources resources, Gson gson,#RefreshTokenClient OkHttpClient okHttpClient){
return new Retrofit.Builder()
.baseUrl(resources.getString(R.string.base_api_url))
.addConverterFactory(GsonConverterFactory.create(gson))
.addCallAdapterFactory(RxJava2CallAdapterFactory.create())
.client(okHttpClient)
.build();
}
Then we can provide our seperated interfaces :
#Provides
#ApplicationScope
public APIService provideApi(#ApiClient Retrofit retrofit) {
return retrofit.create(APIService.class);
}
#Provides
#ApplicationScope
public RefreshTokenApi provideRefreshApi(#RefreshTokenClient Retrofit retrofit) {
return retrofit.create(RefreshTokenApi.class);
}
When providing our TokenAuthenticator :
#Provides
#ApplicationScope
TokenAuthenticator provideTokenAuthenticator(RefreshTokenApi mApi){
return new TokenAuthenticator(mApi);
}
Advantages : You have two seperated api interfaces which means you can maintain them independently. Also you can use plain OkHttp or HttpUrlConnection or another library.
Disadvantages : You will have two different OkHttp and Retrofit instance.
P.S : Make sure you make syncronous calls inside Authenticator class.
You can inject the service dependency into your authenticator via the Lazy type. This way you will avoid the cyclic dependency on instantiation.
Check this link on how Lazy works.
I have a situation where I need two Retrofit Services, each one has its business implementation.
#Provides
#Singleton
#Named("defaultMulhimService")
MulhimService provideMulhimService() {
return MulhimService.Creator.newMulhimService();
}
#Provides
#Singleton
#Named("MulhimServiceWithCache")
MulhimService providesMulhimServiceWithCache(){
return MulhimService.Creator.newMulhimServiceWithCache(mApplication);
}
I'm already looked at this answer which suggest using #Named annotation to differ multiple instances at modules, but what I don't know, how to inject them.
You could use something like this (https://guides.codepath.com/android/Dependency-Injection-with-Dagger-2) -
#Provides #Named("cached")
#Singleton
OkHttpClient provideOkHttpClient(Cache cache) {
OkHttpClient client = new OkHttpClient();
client.setCache(cache);
return client;
}
#Provides #Named("non_cached") #Singleton
OkHttpClient provideOkHttpClient() {
OkHttpClient client = new OkHttpClient();
return client;
}
#Inject #Named("cached") OkHttpClient client;
#Inject #Named("non_cached") OkHttpClient client2;
Basically you inject the instance using the #Named qualifier
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!
#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.
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);
...
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.
Provide different versions of the same type
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();
}
Custom #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).
Injecting the qualified version
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!)