Dagger 2 injecting two retrofit objects

℡╲_俬逩灬. 提交于 2019-12-06 03:55:59

You are right the problem is the second exposed instance of Retrofit in application module.The solution is to use dagger qualifiers. Just replace appropriate code blocks with:

Define retrofit provider with qualifier @Named("streaming") in Application Module

@Provides
@Singleton
@Named("streaming")
Retrofit provideStreamingRetrofit(@Named("ok-1") OkHttpClient client, GsonConverterFactory convectorFactory, RxJava2CallAdapterFactory adapterFactory) {
    return new Retrofit.Builder()
            .baseUrl(Consts.STREAMING_BASE_PATH)
            .addConverterFactory(convectorFactory)
            .addCallAdapterFactory(adapterFactory)
            .client(client)
            .build();
}

Don't foreget to expose retrofit instance with exact same qualifier in Application Component

@Singleton
@Component(modules = ApplicationModule.class)
public interface ApplicationComponent {

    @Named("streaming") Retrofit exposeStreamingRetrofit();

    Retrofit exposeRetrofit();

    Context exposeContext();

    AppPreferenceHelper exposePrefs();

}

Whenever you need the streaming service retrofit instance - don't forget to set qualifier. Example in Streaming Module

@PerFragment
@Provides
StreamingService provideStreamingService(@Named("streaming") Retrofit retrofit) {
    return retrofit.create(StreamingService.class);
}

You can use the @Named annotation as described by @ilosqu but I could not get over the use of magic strings and the resulting code smell. In a large project with many Retrofit instances developers would need to remember, or worse lookup, the Name values each time the instance is required. This seems tedious, unmanageable at scale and difficult to refactor. I am not judging here, I just want to state my reasoning for another approach.

My solution (Kotlin) was to create unique Classes for each Retrofit instance similar to the idea of marker interfaces in Java. Since Retrofit is a final class and does not provide any interfaces to allow for easy extension was forced to create an abstract wrapper class instead which accomplishes the same result.

/**
 * This abstract class allows for extension and can expose 
 * convenience functions to Retrofit functionality by composition
 */

abstract class RetrofitContainer(private val retrofit: Retrofit) {
    fun <T> create(service: Class<T>): T {
        return retrofit.create(service)
    }

    fun getRetrofit(): Retrofit {
        return retrofit
    }
}

For each unique instance of Retrofit you will need to create a concrete class extending RetrofitContainer.

/**
 * This class can be injected anywhere you want the Streaming Retrofit instance
 */
class StreamingRetrofit(retrofit: Retrofit): RetrofitContainer(retrofit)

Now in your Modules you can create @Provides methods for each "marker" class and inject via Class without the need for @Named

@Provides
@Singleton
fun provideStreamingService(retrofit: StreamingRetrofit): StreamingService{
    return retrofit.create(StreamingService::class.java)
}

@Provides
@Singleton
fun provideStreamingRetrofit(
        client: OkHttpClient,
        adapterFactory: RxJava2CallAdapterFactory,
        converterFactory: GsonConverterFactory
): StreamingRetrofit {
    val retrofit = Retrofit.Builder()
            .baseUrl(Consts.STREAMING_BASE_PATH)
            .addConverterFactory(convectorFactory)
            .addCallAdapterFactory(adapterFactory)
            .client(client)
            .build();

    return StreamingRetrofit(retrofit)
}

This technique allows concise lookup and refactoring without needing to remember any magic. It is probably possible to use the same approach in most cases that you would otherwise need to use @Named instances.

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!