How to remove Dagger Hilt dependency Injection cycle - android

I have a network module class that provides an ApiService instance.
There is an Authenticator class which refreshes access token when expired.
The authenticator requires ApiService instance for making API calls.
This causes a cyclic dependency. How to avoid this?
Now I'm creating a new ApiService inside TokenExpiryAuthenticator class to make API calls, to break the cyclic dependency.
How to properly inject ApiService into TokenExpiryAuthenticator without causing cyclic dependency?
#InstallIn(SingletonComponent::class)
#Module
object NetworkModule {
#Provides
#Singleton
#Named("Other")
fun provideRetrofitWithoutInterceptor(#Named("Other") client: OkHttpClient, gson: Gson): Retrofit {
return Retrofit.Builder()
.baseUrl(BuildConfig.BASE_URL)
.client(client)
.addConverterFactory(GsonConverterFactory.create(gson))
.build()
}
#Provides
#Singleton
fun provideRetrofit(client: OkHttpClient, gson: Gson): Retrofit {
return Retrofit.Builder()
.baseUrl(BuildConfig.BASE_URL)
.client(client)
.addConverterFactory(GsonConverterFactory.create(gson))
.build()
}
#Provides
#Singleton
fun providesOkHttpClient(httpLoggingInterceptor: HttpLoggingInterceptor, supportInterceptor: SupportInterceptor, tokenExpiryAuthenticator: TokenExpiryAuthenticator): OkHttpClient {
return OkHttpClient.Builder().writeTimeout(1, TimeUnit.MINUTES)
.readTimeout(1, TimeUnit.MINUTES)
.callTimeout(1, TimeUnit.MINUTES)
.addInterceptor(httpLoggingInterceptor)
.addInterceptor(supportInterceptor)
.authenticator(tokenExpiryAuthenticator)
.build()
}
#Named("Other")
#Provides
#Singleton
fun providesOkHttpClientWithoutInterceptor(httpLoggingInterceptor: HttpLoggingInterceptor): OkHttpClient {
return OkHttpClient.Builder().writeTimeout(1, TimeUnit.MINUTES)
.readTimeout(1, TimeUnit.MINUTES)
.callTimeout(1, TimeUnit.MINUTES)
.addInterceptor(httpLoggingInterceptor)
.build()
}
#Provides
#Singleton
fun providesHttpLoggingInterceptor(): HttpLoggingInterceptor {
return if (BuildConfig.DEBUG)
HttpLoggingInterceptor().apply {
level = HttpLoggingInterceptor.Level.BODY
}
else
HttpLoggingInterceptor().apply {
level = HttpLoggingInterceptor.Level.NONE
}
}
#Provides
#Singleton
fun providesGson(): Gson {
return GsonBuilder().create()
}
#Provides
#Singleton
fun providesRestApiService(retrofit: Retrofit): ApiService {
return retrofit.create(ApiService::class.java)
}
#Provides
#Singleton
#Named("Other")
fun providesRestApiServiceWithoutInterceptor(#Named("Other") retrofit: Retrofit): ApiService{
return retrofit.create(ApiService::class.java)
}
}

You could split your ApiService and create a new AuthenticationApi which only includes the endpoints to authenticate or refresh your access tokens. This AuthenticationApi is created with an OkHttp instance without your Authenticator.
That way your Authenticator only needs reference to this slim Retrofit api.
This also guarantees that you don't get HTTP 401 authentication loops in case of wrong credentials when using your authentication endpoints.

Simplest possible solution is to inject the dependency lazily.
class TokenExpiryAuthenticator #Inject constructor(
private val api: Lazy<ApiService>,
private val persistence: TokenPersistenceRepository
) : Authenticator {
override fun authenticate(route: Route?, response: Response): Request? {
// If HTTP 401 error was raised while trying to refresh token it means
// the token is invalid (or expired). Sign out user and do not
// proceed with further requests.
if (response.request.url.encodedPath == REFRESH_TOKEN_PATH) {
// Perform any cleanup needed.
// Return null so no further requests will be performed.
return null
}
// Request failed for old token. It's about time to refresh it.
if (response.request.header(AUTH_HEADER) != null) {
// Refresh access token using your lazily injected service.
persistence
.retrieveRefreshToken()
.flatMap(api.get()::refreshToken)
.flatMapCompletable {
Completable.concatArray(
persistence.saveAccessToken(it.accessToken),
persistence.saveRefreshToken(it.refreshToken),
)
}
.blockingAwait()
// You can use blocking await as the [authenticate] method is
// run on I/O thread anyway.
}
// Load recently refreshed access token.
val token = (...)
// Format authorization header value.
val header = (...)
// Modify and proceed with a request with refreshed access token.
return response.request.newBuilder()
.removeHeader(AUTH_HEADER)
.addHeader(AUTH_HEADER, header)
.build()
}
}

Related

Passing an interceptor to retrofit builder using dagger-hilt

I'm learning about retrofit interceptors, for work purposes I'm using dagger-hilt for the injection of dependencies to fragments etc. I wrote a custom interceptor to check for connection errors and I'm trying to add it to the Retrofit.Builder():
#Provides
#Singleton
fun provideApi(): StoreApi {
return Retrofit.Builder()
.baseUrl(Constants.BASE_URL)
.addConverterFactory(GsonConverterFactory.create())
.addCallAdapterFactory(RxJava3CallAdapterFactory.create())
.build()
.create(StoreApi::class.java)
}
however, I have no clue how to pass that:
val okHttpClient = OkHttpClient()
.newBuilder()
.addInterceptor(ConnectivityInterceptor)
.build()
as a .client() to the retofit builder (even with dagger-hilt), any ideas?
You can setup a module something like this where you provide all the dependency requirements as functions and exposing them to dagger through #Provides then leave dagger to provide the dependencies as function arguments to build the dependency graph :
#Module class ApiModule {
#Provides
#Singleton
internal fun provideApi(retrofit: Retrofit): StoreApi {
return retrofit
.create(StoreApi::class.java)
}
#Provides
#Singleton
internal fun retrofit(client: OkHttpClient): Retrofit =
Retrofit.Builder()
.client(client)
.baseUrl(Constants.BASE_URL)
.addConverterFactory(GsonConverterFactory.create())
.addCallAdapterFactory(RxJava3CallAdapterFactory.create())
.build()
#Provides
#Singleton
internal fun client(connectivityInterceptor: ConnectivityInterceptor): OkHttpClient =
OkHttpClient.Builder()
.addInterceptor(connectivityInterceptor)
.build()
#Provides
#Singleton
internal fun interceptor(): ConnectivityInterceptor = ConnectivityInterceptor()
}
This is a trivial example based on the supplied code.

Hilt - Should I #Provides my Base URL or API Key?

I am new to Hilt and have negligible experience in Dagger 2 as well.
I am now doing a simple sample project for my self-learning, and trying to apply Hilt.
I use Retrofit to call APIs.
So, I am trying to refactor my code into using Hilt:
#Module
#InstallIn(SingletonComponent::class)
object WeatherAPIModule {
private const val BASE_URL = "https://api.openweathermap.org/data/2.5/"
#Provides
fun provideHttpLoggingInterceptor(): HttpLoggingInterceptor {
return HttpLoggingInterceptor().apply {
level = HttpLoggingInterceptor.Level.BODY
}
}
#Provides
fun provideOkHttpClient(loggingInterceptor: HttpLoggingInterceptor): OkHttpClient {
return OkHttpClient.Builder()
.addInterceptor(loggingInterceptor)
.build()
}
#Provides
fun provideGsonConverterFactory(): GsonConverterFactory {
return GsonConverterFactory.create()
}
#Provides
fun provideRetrofit(okHttpClient: OkHttpClient, gsonConverterFactory: GsonConverterFactory): Retrofit {
return Retrofit.Builder()
.baseUrl(BASE_URL)
.client(okHttpClient)
.addConverterFactory(gsonConverterFactory)
.build()
}
#Provides
fun provideWeatherService(retrofit: Retrofit): OpenWeatherService {
return retrofit.create(OpenWeatherService::class.java)
}
val weatherApiKey by lazy {
ApiKeyStore.getWeatherApiKey()
}
}
My question is, Should I use #Provides on Base URL and API Key as well?
Why do I ask
According to my understanding, if I have a class like this:
class SomeClass #Inject constructor() {
#Inject lateinit var someString: String
...
}
Hilt will try to find modules that provides String, and inject that String here. But clearly that's not what I want.
What should be the best way to do this?
Especially for weatherApiKey. I think I should not have leave it as an ordinary function, because that would lose the point of using Hilt at all.

Re-initialization an object provided by Dagger 2

I have an interface called LoginService which is used with Retrofit. There was no accessToken before the user login. When the user has logged in, the LoginService Instance should be updated with acessToken so that the user could log out from the app. The problem is that LoginService class is not updated even though it is not declared as #Singleton. If the user has closed the app and reopen it again, the LoginService got updated and therefore he could log out of the app. How can I reinitialize the LoginService instance as soon as the accessToken has been updated?
The key here is not the reinitialize the instance but to create a separate service whenever the user logs in.
you need to have two different interfaces one for API's pre-login and other for post login.
So one would be Authapi.kt and the other would be Api.kt
So first we need to create OkHttpBuilder for each service,
#Provides
#Singleton
#Named(NetModule.NO_AUTH_CLIENT)
fun provideNoAuthOkHttpClient(
okHttpClientBuilder: OkHttpClient.Builder
): OkHttpClient {
return okHttpClientBuilder.build()
}
#Provides
#Singleton
#Named(NetModule.AUTH_CLIENT)
fun provideAuthOkHttpClient(
okHttpClientBuilder: OkHttpClient.Builder,
tokenInterceptor: NetworkInterceptor
): OkHttpClient {
return okHttpClientBuilder.addInterceptor(tokenInterceptor).build()
}
Network Interceptor is the class where you update your access token for API.
NetworkInterceptor.kt
class NetworkInterceptor #Inject constructor(
val context: Context,
serverBaseUrl: String,
val moshi: Moshi,
val preferences: PreferenceUtility
) : Interceptor {
private fun Request.Builder.setDefaultHeaders(): Request.Builder {
addHeader("App_version_code", BuildConfig.VERSION_CODE.toString())
addHeader("App_version_name", BuildConfig.VERSION_NAME)
addHeader("Mobile_model", Build.MODEL.toString())
addHeader("OS_version", Build.VERSION.SDK_INT.toString())
addHeader("OS_version_release", Build.VERSION.RELEASE.toString())
if (preferences.customerId != -1L)
addHeader("Client_id", preferences.customerId.toString())
return this
}
private fun makeRequestWithAuthTokenAndTimeStamp(request: Request) = request.newBuilder()
.setDefaultHeaders()
.apply {
val oldHeader = request.header("Authorization")
if (oldHeader.isNullOrBlank()) {
val token = if (preferences.authToken.isBlank() || !preferences.customerAuthenticated) BuildConfig.ANONYMOUS_TOKEN else preferences.authToken
addHeader("Authorization", "Bearer $token")
}
}
.url(request.url())
.method(request.method(), request.body())
.build()
override fun intercept(chain: Interceptor.Chain): Response {
val request = chain.request()
// ADD THE TOKEN'S OVER HERE
var response =
chain.proceed(makeRequestWithAuthTokenAndTimeStamp(request))
//You can also add refersh logic here.
return response
}
}
And you NetworkModule.kt will be
#Provides
#Singleton
#Named(NetModule.NO_AUTH_CLIENT)
fun provideNoAuthInterceptorRetrofit(
moshi: Moshi,
#Named(NetModule.NO_AUTH_CLIENT) okHttpClient: OkHttpClient,
debugPreferenceUtility: DebugPreferenceUtility
): Retrofit {
return Retrofit.Builder()
.addConverterFactory(MoshiConverterFactory.create(moshi))
.addCallAdapterFactory(RxJava2CallAdapterFactory.create())
.baseUrl(debugPreferenceUtility.serverBaseUrl)
.client(okHttpClient)
.build()
}
#Provides
#Singleton
#Named(NetModule.AUTH_CLIENT)
fun provideAuthInterceptorRetrofit(
moshi: Moshi,
#Named(NetModule.AUTH_CLIENT) okHttpClient: OkHttpClient,
debugPreferenceUtility: DebugPreferenceUtility
): Retrofit {
return Retrofit.Builder()
.addConverterFactory(MoshiConverterFactory.create(moshi))
.addCallAdapterFactory(RxJava2CallAdapterFactory.create())
.baseUrl(debugPreferenceUtility.serverBaseUrl)
.client(okHttpClient)
.build()
}
#Provides
#Singleton
fun provideApi(#Named(AUTH_CLIENT) retrofit: Retrofit) = retrofit.create(Api::class.java)
#Provides
#Singleton
fun provideAuthApi(#Named(NO_AUTH_CLIENT) retrofit: Retrofit) = retrofit.create(AuthApi::class.java)
So the API's that will be used pre-login will be placed inside AuthApi.kt
like send OTP etc and all the rest API will be placed inside Api.kt and Network Interceptor will take care of adding token.

Dagger2 generating multiple instances of retrofit interceptor

Dagger 2 is generating multiple instances of retrofit interceptor despite marking it as singleton in dagger module. Now the problem is that AuthorizationInterceptor constructor gets called twice which I don't understand why and because of that the headers that I set after getting result from login API get sets to a different instance of Interceptor and while making call to some other API which requires authorizationToken the token is unset.
Here is my ApiModule
#Module
open class ApiModule {
#Provides
#Singleton
fun provideHttpLoggingInterceptor(): HttpLoggingInterceptor {
val loggingInterceptor = HttpLoggingInterceptor()
loggingInterceptor.level = HttpLoggingInterceptor.Level.BODY
return loggingInterceptor
}
#Provides
#Singleton
fun provideHeaderInterceptor(): Interceptor {
return AuthorizationInterceptor()
}
#Provides
#Singleton
fun provideHttpClient(interceptor: HttpLoggingInterceptor, headerInterceptor: Interceptor): OkHttpClient {
return OkHttpClient.Builder()
.addInterceptor(interceptor)
.addInterceptor(headerInterceptor)
.build()
}
#Provides
#Singleton
fun provideMoshi(): Moshi {
return Moshi.Builder()
.build()
}
#Provides
#Singleton
fun provideRetrofit(client: OkHttpClient, moshi: Moshi, apiConfig: ApiConfig): Retrofit {
return Retrofit.Builder()
.baseUrl(apiConfig.baseUrl)
.client(client)
.addCallAdapterFactory(RxJava2CallAdapterFactory.create())
.addConverterFactory(MoshiConverterFactory.create(moshi))
.build()
}
#Provides
#Singleton
fun provideFrappApi(retrofit: Retrofit): FrappApi {
return retrofit.create(FrappApi::class.java)
}
Here is my AuthorizationInterceptor class
#Singleton
class AuthorizationInterceptor #Inject constructor() : Interceptor {
override fun intercept(chain: Interceptor.Chain?): Response {
val request = chain?.request()
val requestBuilder = request?.newBuilder()
if (request?.header("No-Authorization") == null && authorization.isNotEmpty()) {
requestBuilder?.addHeader("Authorization", authorization)
}
return chain?.proceed(requestBuilder!!.build())!!
}
private var authorization: String = ""
fun setSessionToken(sessionToken: String) {
this.authorization = sessionToken
}
}
You dont need to make a provide method if you do a constructor injection.
Remove the provideHeaderInterceptor method, then update the provideHttpClient method like below,
#Provides
#Singleton
fun provideHttpClient(interceptor: HttpLoggingInterceptor,
headerInterceptor: AuthorizationInterceptor): OkHttpClient {
return OkHttpClient.Builder()
.addInterceptor(interceptor)
.addInterceptor(headerInterceptor)
.build()
}
Or if you dont like the solution above, you can remove the #Singleton and #Inject in your AuthorizationInterceptor class.

Android Dagger2 + OkHttp + Retrofit dependency cycle error

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.

Categories

Resources