Как решить циклические зависимости в Dagger (Hilt)

Я хочу обновить свой токен с помощью перехватчика, но моему перехватчику требуется служба API для выполнения вызовов API. Я застрял в цикле зависимости.

Вот мой класс ApplicationModule:

@Module
@InstallIn(ApplicationComponent::class)
class ApplicationModule {


    @Provides
    fun providerBaseUrl() = AppConstants.BASE_URL

    @Provides
    @Singleton
    fun provideOkHttpClient(authInterceptor: AuthInterceptor,
                            networkInterceptor: NetworkInterceptor) = if (BuildConfig.DEBUG) {

        val loggingInterceptor = HttpLoggingInterceptor()
        loggingInterceptor.setLevel(HttpLoggingInterceptor.Level.BODY)
        OkHttpClient.Builder()
                .addInterceptor(authInterceptor)
                .addInterceptor(loggingInterceptor)
                .addInterceptor(networkInterceptor)
                //   .addInterceptor(refreshTokenInterceptor) // I want to put my interceptor here
                .connectTimeout(2, TimeUnit.MINUTES)
                .readTimeout(2, TimeUnit.MINUTES)
                .writeTimeout(2, TimeUnit.MINUTES)

                .build()
    } else OkHttpClient
            .Builder()
            .connectTimeout(2, TimeUnit.MINUTES)
            .readTimeout(2, TimeUnit.MINUTES)
            .writeTimeout(2, TimeUnit.MINUTES)
            .addInterceptor(authInterceptor)
            .addInterceptor(networkInterceptor)
            .build()


    @Provides
    @Singleton
    fun provideRetrofit(
            okHttpClient: OkHttpClient,
            BASE_URL: String
    ): Retrofit =
            Retrofit.Builder()
                    .addConverterFactory(MoshiConverterFactory.create())
                    .baseUrl(BASE_URL)
                    .client(okHttpClient)
                    .build()

    @Provides
    @Singleton
    fun provideApiService(retrofit: Retrofit): WebApi = retrofit.create(WebApi::class.java)


      @Provides
      @Singleton // this is my provider
      fun provideRefreshTokenService(webApi: WebApi): RefreshTokenInterceptor {
          return RefreshTokenInterceptor(webApi)
      }

    @Provides
    fun provideAuthInterceptor(): AuthInterceptor {
        return AuthInterceptor()
    }

    @Provides
    fun provideNetWorkInterceptor(): NetworkInterceptor {
        return NetworkInterceptor()
    }
}

А это мой RefreshTokenInterceptor:

class RefreshTokenInterceptor @Inject constructor(webApi: WebApi) : Interceptor {

    var api = webApi

    override fun intercept(chain: Interceptor.Chain): Response {
        val requestBuilder = chain.request().newBuilder()
        val updatedToken = getUpdatedToken(webApi = api)
        requestBuilder.header("Authorization", updatedToken)

        return chain.proceed(requestBuilder.build())
    }

    private fun getUpdatedToken(webApi: WebApi): String {

        GlobalScope.launch {
            val authTokenResponse = webApi.refreshToken()
            val newToken = "${authTokenResponse.body()!!.data.token}"
            SharedPref.getInstance(AppController.applicationContext()).setUserToken(newToken)

        }
        return SharedPref.getInstance(AppController.applicationContext()).getUserToken
    }
}

person Waheed Khan    schedule 21.01.2021    source источник
comment
Ваш RefreshTokenInterceptor хочет, чтобы WebApi выполнял вызов API. Теперь, чтобы создать экземпляр WebApi, нам понадобится Retrofit Instance. Для создания модифицированного экземпляра нам понадобится экземпляр OkHttp. Чтобы создать экземпляр OkHttp, нам снова понадобится AuthInterceptor, NetworkInterceptor, а также RefreshTokenInterceptor. Чтобы построить RefreshTokenInterceptor, нам снова нужен экземпляр WebApi, поэтому здесь он стал циклическим.   -  person Kishan Maurya    schedule 21.01.2021


Ответы (2)


Взгляните на этот пример:

@Provides
@Singleton
fun provideDatabase(
    @ApplicationContext context: Context,
    @RoomCreateCallback callback: RoomDatabase.Callback
) = Room.databaseBuilder(context, TaskDatabase::class.java, Constants.TaskKeys.TASK_DATABASE)
    .fallbackToDestructiveMigration()
    .addCallback(callback)
    .build()

@Provides
fun provideTaskDao(taskDatabase: TaskDatabase) = taskDatabase.taskDao()

@Provides
@Singleton
@RoomCreateCallback
fun provideDatabaseCreateCallback(
    taskDatabase: Provider<TaskDatabase>,
    @ApplicationScope applicationScope: CoroutineScope
) = object : RoomDatabase.Callback() {
    override fun onCreate(db: SupportSQLiteDatabase) {
        super.onCreate(db)

        val dao = taskDatabase.get().taskDao()

        applicationScope.launch {
            dao.insert(Task("First task"))
            dao.insert(Task("Second task"))
            dao.insert(Task("Third task", important = true))
            dao.insert(Task("Fourth task", completed = true))
            dao.insert(Task("Fifth task"))
            dao.insert(Task("Sixth task", completed = true))
            dao.insert(Task("Seventh task"))
            dao.insert(Task("Eighth task"))
        }
    }
}

Мне нужен RoomDatabase.Callback при создании RoomDatabase, но мне также нужна RoomDatabase при выполнении этого обратного вызова. В функции provideDatabaseCreateCallback () я обернул RoomDatabase внутри Provider ‹›.

person Matija Sokol    schedule 21.01.2021
comment
Я немного понимаю это, но в моем случае, когда я использую поставщика в конструкторе. Я просто изучаю внедрение зависимостей, поэтому у меня мало знаний .. - person Waheed Khan; 22.01.2021

Я столкнулся с аналогичной проблемой и нашел решение. Вы можете обернуть экземпляр api в свой Token Interceptor с помощью ленивого интерфейса. Это решит вашу проблему с циклической зависимостью.

Вот фрагмент кода.

class RefreshTokenInterceptor @Inject constructor(private val webApi: Lazy<WebApi>) : Interceptor {

    override fun intercept(chain: Interceptor.Chain): Response {
        val requestBuilder = chain.request().newBuilder()
        val updatedToken = getUpdatedToken(webApi = api.get())
        requestBuilder.header("Authorization", updatedToken)

        return chain.proceed(requestBuilder.build())
    }

    private fun getUpdatedToken(webApi: WebApi): String {

        GlobalScope.launch {
            val authTokenResponse = webApi.refreshToken()
            val newToken = "${authTokenResponse.body()!!.data.token}"
            SharedPref.getInstance(AppController.applicationContext()).setUserToken(newToken)

        }
        return SharedPref.getInstance(AppController.applicationContext()).getUserToken
    }
} 
person Joydeep Das    schedule 25.01.2021
comment
я пробую это, но это не работает ... - person Waheed Khan; 27.01.2021
comment
Какую ошибку вы получаете? - person Joydeep Das; 27.01.2021
comment
тот же цикл зависимости от ошибок ... - person Waheed Khan; 27.01.2021