3

I had the "typical" issue of dependency cycle error trying to make a call to refresh token in an Interceptor. I found some related questions and answers, specially this one and this one. I followed them and it made the code compiling.

But it doesn't work because the 'ServiceHolder' is always empty (created without the service), so I guess I'm doing something wrong with Dagger and I don't know what.

I have the 'AuthServiceHolder':

class AuthServiceHolder {

    internal var authService: AuthService? = null

    fun get(): AuthService? {
        return authService
    }

    fun set(authService: AuthService) {
            this.authService = authService
    }
}

I have the 'TokenExpiredInterceptor' (first lines):

@TokenExpiredInterceptorScope
class TokenExpiredInterceptor @Inject constructor(private val authServiceHolder: AuthServiceHolder
) : Interceptor

I try to get the service to make the call but is always null in 'TokenExpiredInterceptor':

val authService = authServiceHolder.get()

I have 2 instances of retroft, one for a legacy API and another for the new API. The common module is:

@Module
object CommonApiModule {

    @JvmStatic
    internal fun retrofitBuilder(okHttpClient: OkHttpClient, baseUrl: HttpUrl) = Retrofit.Builder()
            .baseUrl(baseUrl)
            .client(okHttpClient)
(...)
}

The module where I create the retrofit for the new API is:

@Module
class MyMicroservicesApiModule(baseUrl: String) : BaseApiModule(baseUrl) {
    @Provides
    @ForNetworkApi(NetworkApiType.MY_MICROSERVICES)
    @NetworkScope
    internal fun retrofit(okHttpClient: OkHttpClient,
                          callAdapterFactory: RxJava2CallAdapterFactory,
                          moshi: Moshi): Retrofit =
            CommonApiModule.retrofitBuilder(okHttpClient, httpUrl)
                    .addCallAdapterFactory(callAdapterFactory)
                    .addConverterFactory(MoshiConverterFactory.create(moshi))
                    .build()

}

The module with the interceptor is:

@Module(includes = [
    (...)
])
abstract class AuthTokenBindingsModule {

    @Binds
    @IntoSet
    @ForNetworkInterceptors
    internal abstract fun tokenExpiredInterceptor(tokenExpiredInterceptor: TokenExpiredInterceptor): Interceptor

    (...)

    @Module
    companion object {
        (...)
        @Provides
        @JvmStatic
        @NetworkScope
        fun authServiceHolder(): AuthServiceHolder = AuthServiceHolder()
    }
}

The module for create the httpClient is:

@Module(includes = [HttpInterceptorsModule::class])
object HttpModule {
    private const val CONNECTION_TIMEOUT = 25
    private const val READ_CONNECTION_TIMEOUT = 25
    private const val WRITE_CONNECTION_TIMEOUT = 25

    private const val DISK_CACHE_SIZE: Long = 50 * 1024 * 1024

    @JvmStatic
    @Provides
    @NetworkScope
    internal fun httpClient(application: Application,
                            @ForNetworkInterceptors networkInterceptors: Set<@JvmSuppressWildcards Interceptor>,
                            @ForHttpInterceptors httpInterceptors: Set<@JvmSuppressWildcards Interceptor>): OkHttpClient {
        val builder = httpClientBuilder(application)
        httpInterceptors.forEach { it -> builder.addInterceptor(it) }
        networkInterceptors.forEach { it -> builder.addNetworkInterceptor(it) }

        if (BuildConfig.DEBUG) {
            val loggingInterceptor = HttpLoggingInterceptor()
            loggingInterceptor.level = HttpLoggingInterceptor.Level.BODY
            builder.addNetworkInterceptor(loggingInterceptor)
        }
        return builder.build()
    }

    @JvmStatic
    private fun httpClientBuilder(application: Application): OkHttpClient.Builder {
        val builder = OkHttpClient.Builder()
                .connectTimeout(CONNECTION_TIMEOUT.toLong(), TimeUnit.SECONDS)
                .readTimeout(READ_CONNECTION_TIMEOUT.toLong(), TimeUnit.SECONDS)
                .writeTimeout(WRITE_CONNECTION_TIMEOUT.toLong(), TimeUnit.SECONDS)

        val cacheDir = File(application.cacheDir, "http")
        val cache = Cache(cacheDir, DISK_CACHE_SIZE)
        builder.cache(cache)
        return builder
    }
}

Finally, my network component is:

@NetworkScope
@TokenExpiredInterceptorScope
@AuthenticatorScope
@Component(modules = [
    (...)
    CommonApiModule::class,
    MyMicroservicesApiModule::class,
    AuthTokenBindingsModule::class,
    (...)
], dependencies = [AppServiceProvider::class])
interface NetworkComponent :
        (...)
        MyMicroservicesApiProvider,
        (...) {

With this code I have an instance of 'AuthServiceHolder' in the 'TokenExpiredInterceptor', it is NOT null but it is empty. I saw in the links above I have to refactor the 'AuthService' provider with something like this:

    @Provides
    @NetworkScope
    internal fun authService(@ForNetworkApi(NetworkApiType.MY_MICROSERVICES) retrofit: Retrofit, authServiceHolder: AuthServiceHolder): AuthService {
        val authService = retrofit.create(AuthService::class.java)
        authServiceHolder.set(authService)
        return authService
    }`

But I don't know where put it. I've tried in 'AuthTokenBindingsModule', in 'MyMicroservicesApiModule', with no success, it is always empty.

I've omitted several lines of code to make it simpler and for privacy.

Any idea? Thanks in advance.

SergiBC
  • 513
  • 6
  • 21
  • I think I know the problem. I think 'AuthService' is never injected anywhere, then it is never created and not set to any holder.. When 'AuthService' is injected (i.e. in a repository even if it is no used), then everything seems ok. It is not a solution, it is a workaround... Any idea? – SergiBC May 10 '19 at 22:32
  • 1
    did you find a solution? – kerollos Jun 25 '20 at 10:48

0 Answers0