当使用 koin 注入 UseCase 时,方法在 android 中抛出 'java.lang.StackOverflowError' 异常

Method threw 'java.lang.StackOverflowError' exception in android when inject UseCase with koin

当我尝试在域包中的 repositoryImpl 中注入此行时:

 private val userUseCase: UserUseCase by inject()

我收到这个错误:

java.lang.WhosebugError: stack size 1040KB

我的包结构是: - 数据 -> 用于改造、房间和... - 域 -> 存储库在这里实现并在数据和表示之间建立连接 - 介绍 -> UI

我认为是因为 DI 和 Koin 提供者中的执行不当。这是我的 network-provider 和 network-module:

网络提供商:

    /**
 * provide HttpLoggingInterceptor for dependency injection with *Koin*
 *
 * @return the HttpLoggingInterceptor object <HttpLoggingInterceptor>
 *
 * @see HttpLoggingInterceptor
 */
fun provideLoggingInterceptor(): HttpLoggingInterceptor {
    val logger = HttpLoggingInterceptor()
    logger.level = HttpLoggingInterceptor.Level.BASIC
    return logger
}


/**
 * provide OkHttpClient for dependency injection with *Koin*
 *
 * @param loggingInterceptor: a HttpLoggingInterceptor object, injected
 * @param cache: a Cache object, injected
 * @param hostnameVerifier: a HostnameVerifier object, injected
 *
 * @return the OkHttpClient built object <OkHttpClient>
 *
 * @see HttpLoggingInterceptor
 * @see Cache
 * @see HostnameVerifier
 */
fun provideOkHttpClient(
    loggingInterceptor: HttpLoggingInterceptor,
    cache: Cache,
    hostnameVerifier: HostnameVerifier
): OkHttpClient {

    val clientBuilder = OkHttpClient()
        .newBuilder()
        .followRedirects(true)
        .followSslRedirects(true)
        .retryOnConnectionFailure(true)
        .connectTimeout(10, TimeUnit.SECONDS)
        .readTimeout(10, TimeUnit.SECONDS)
        .writeTimeout(10, TimeUnit.SECONDS)
        .hostnameVerifier(hostnameVerifier)
        .addInterceptor(loggingInterceptor)
        .cache(cache)

    return clientBuilder.build()
}


/**
 * provide provideOkHttpClientForRefresh for dependency injection with *Koin*
 *
 * @param loggingInterceptor: a HttpLoggingInterceptor object, injected
 * @param cache: a Cache object, injected
 * @param hostnameVerifier: a HostnameVerifier object, injected
 *
 * @return the OkHttpClient built object <OkHttpClient>
 *
 * @see HttpLoggingInterceptor
 * @see Cache
 * @see HostnameVerifier
 */
fun provideOkHttpClientForRefresh(
    loggingInterceptor: HttpLoggingInterceptor,
    refreshTokenInterceptor: RefreshTokenInterceptor,
    cache: Cache,
    hostnameVerifier: HostnameVerifier
): OkHttpClient {

    val clientBuilder = OkHttpClient()
        .newBuilder()
        .followRedirects(true)
        .followSslRedirects(true)
        .retryOnConnectionFailure(true)
        .connectTimeout(10, TimeUnit.SECONDS)
        .readTimeout(10, TimeUnit.SECONDS)
        .writeTimeout(10, TimeUnit.SECONDS)
        .hostnameVerifier(hostnameVerifier)
        .addInterceptor(loggingInterceptor)
        .addInterceptor(refreshTokenInterceptor)
        .cache(cache)

    return clientBuilder.build()
}


/**
 * provide OkHttpClient for dependency injection with *Koin*
 *
 * @param loggingInterceptor: a HttpLoggingInterceptor object, injected
 * @param accessTokenInterceptor: a AccessTokenInterceptor object, injected
 * @param accessTokenAuthenticator: a AccessTokenAuthenticator object, injected
 * @param cache: a Cache object, injected
 * @param hostnameVerifier: a HostnameVerifier object, injected
 *
 * @return the OkHttpClient built object <OkHttpClient>
 *
 * @see HttpLoggingInterceptor
 * @see AccessTokenInterceptor
 * @see AccessTokenAuthenticator
 * @see Cache
 * @see HostnameVerifier
 */

fun provideOkHttpClientForAuth(
    loggingInterceptor: HttpLoggingInterceptor,
    accessTokenInterceptor: AccessTokenInterceptor,
    accessTokenAuthenticator: AccessTokenAuthenticator,
    cache: Cache,
    hostnameVerifier: HostnameVerifier
): OkHttpClient {

    val clientBuilder = OkHttpClient()
        .newBuilder()
        .followRedirects(true)
        .followSslRedirects(true)
        .retryOnConnectionFailure(true)
        .connectTimeout(10, TimeUnit.SECONDS)
        .readTimeout(10, TimeUnit.SECONDS)
        .writeTimeout(10, TimeUnit.SECONDS)
        .hostnameVerifier(hostnameVerifier)
        .authenticator(accessTokenAuthenticator)
        .addInterceptor(accessTokenInterceptor)
        .addInterceptor(loggingInterceptor)
        .cache(cache)

    return clientBuilder.build()
}


/**
 * provide Cache for dependency injection with *Koin*
 *
 * @param cacheDirection: a CacheDirection object, injected
 *
 * @return the Cache object <Cache>
 *
 *  @see CacheDirection
 *  @see Cache
 */
fun provideCache(cacheDirection: CacheDirection): Cache {

    val cacheFile = createDefaultCacheDir(cacheDirection.path, "api_cache")

    return Cache(cacheFile, calculateDiskCacheSize(cacheFile))
}


/**
 * provide HostnameVerifier for dependency injection with *Koin*
 *
 * @return the HostnameVerifier object <HostnameVerifier>
 *
 *  @see HostnameVerifier
 */
fun provideHostnameVerifier(): HostnameVerifier {

    return HostnameVerifier { hostname, _ ->

        return@HostnameVerifier BuildConfig.API_URL_V1.contains(hostname)
    }
}

/**
 * provide Retrofit for dependency injection with *Koin*
 *
 * @param okHttpClient: a OkHttpClient object, injected
 *
 * @return the Retrofit built object <Retrofit>
 *
 * @see OkHttpClient
 * @see Retrofit
 */
fun provideRetrofitForAuth(okHttpClient: OkHttpClient): Retrofit {
    return Retrofit.Builder().baseUrl(BuildConfig.API_URL_V1).client(okHttpClient)
        .addConverterFactory(GsonConverterFactory.create()).build()
}

/**
 * provide Retrofit for dependency injection with *Koin*
 *
 * @param okHttpClient: a OkHttpClient object, injected
 *
 * @return the Retrofit built object <Retrofit>
 *
 * @see OkHttpClient
 * @see Retrofit
 */
fun provideRetrofit(okHttpClient: OkHttpClient): Retrofit {
    return Retrofit.Builder().baseUrl(BuildConfig.API_URL_V1).client(okHttpClient)
        .addConverterFactory(GsonConverterFactory.create()).build()
}


/**
 * provide Retrofit for dependency injection with *Koin*
 *
 * @param okHttpClient: a OkHttpClient object, injected
 *
 * @return the Retrofit built object <Retrofit>
 *
 * @see OkHttpClient
 * @see Retrofit
 */
fun provideOkHttpClientForRefresh(okHttpClient: OkHttpClient): Retrofit {
    return Retrofit.Builder().baseUrl(BuildConfig.API_URL_V1).client(okHttpClient)
        .addConverterFactory(GsonConverterFactory.create()).build()
}


/**
 * provide AuthApi for dependency injection with *Koin*
 *
 * @param retrofit: a Retrofit object, injected
 *
 * @return object created retrofit from retrofit's interface <AuthApi>
 *
 * @see Retrofit
 * @see AuthApi
 */
fun provideAuthApi(retrofit: Retrofit): AuthApi = retrofit.create(AuthApi::class.java)


/**
 * provide CheckTokenApi for dependency injection with *Koin*
 *
 * @param retrofit: a Retrofit object, injected
 *
 * @return object created retrofit from retrofit's interface <AuthApi>
 *
 * @see Retrofit
 * @see CheckTokenApi
 */
fun provideCheckTokenApi(retrofit: Retrofit): CheckTokenApi =
    retrofit.create(CheckTokenApi::class.java)


/**
 * provide AuthApi for dependency injection with *Koin*
 *
 * @param retrofit: a Retrofit object, injected
 *
 * @return object created retrofit from retrofit's interface <AuthApi>
 *
 * @see Retrofit
 * @see AuthApi
 */
fun provideUserApi(retrofit: Retrofit): UserApi = retrofit.create(UserApi::class.java)

网络模块:

    /**
 * Create network provider module for dependency injection with *Koin*
 *
 * @see provideRetrofit
 * @see provideOkHttpClient
 * @see provideLoggingInterceptor
 * @see provideAuthApi
 * @see ResponseHandler
 * @see AccessTokenInterceptor
 * @see AccessTokenAuthenticator
 */


val networkModule = module {
    factory { provideLoggingInterceptor() }
    factory { provideCache(get()) }
    factory { provideHostnameVerifier() }
    factory { ResponseHandler() }
    factory { AccessTokenInterceptor(get()) }
    factory { AccessTokenAuthenticator(get(), get()) }
    factory { RefreshTokenInterceptor(get(), get()) }

    factory(named("allRequestOkHttpClient")) {
        provideOkHttpClient(
            get(),
            get(),
            get()
        )
    }


    factory(named("refreshTokenRequestOkHttpClient")) {
        provideOkHttpClientForRefresh(
            get(),
            get(),
            get(),
            get()
        )
    }


    factory(named("authRequestOkHttpClient")) {
        provideOkHttpClientForAuth(
            get(), get(), get(), get(), get()
        )
    }

    single(named("allRequestRetrofit")) { provideRetrofit(get(named("allRequestOkHttpClient"))) }
    single(named("refreshTokenRequestRetrofit")) { provideOkHttpClientForRefresh(get(named("refreshTokenRequestOkHttpClient"))) }
    single(named("authRequestRetrofit")) { provideRetrofitForAuth(get(named("authRequestOkHttpClient"))) }


    factory { provideAuthApi(get(named("allRequestRetrofit"))) }
    factory { provideCheckTokenApi(get(named("refreshTokenRequestRetrofit"))) }
    factory { provideUserApi(get(named("authRequestRetrofit"))) }

}

AccessTokenAuthenticator:

  /**
 * a Authenticator class for add *Authorization* header into *okhttp* request
 * implement from Authenticator (okhttp3.Authenticator) and KoinComponent (org.koin.core.KoinComponent)
 */
class AccessTokenAuthenticator(
    private val checkTokenApi: CheckTokenApi,
    private val userPreferences: UserPreferences
) : Authenticator {

    /**
     * override function for handle add *Authorization* into *okhttp* request
     *
     * @param route
     * @param response
     *
     * @return Request
     */
    @Nullable
    override fun authenticate(route: Route?, response: Response): Request? {

        synchronized(this) {

            val newAccessToken =
                checkTokenApi.checkToken().execute().body()

            return if (userPreferences.token != newAccessToken?.apiObjects?.user?.token) {
                if (newAccessToken?.apiObjects?.user?.token?.isNotEmpty() == true) {
                    userPreferences.token = newAccessToken.apiObjects.user.token
                }
                newRequestWithAccessToken(
                    response.request(),
                    newAccessToken?.apiObjects?.user?.token ?: ""
                )
            } else {

                newRequestWithAccessToken(response.request(), userPreferences.token)
            }

        }
    }


    /**
     * create request with custom header(Device-Id, Device-Token)
     *
     * @param request request for add header <Request>
     * @param accessToken for add Uer-Token header provided from server <String>
     *
     * @return a Request with custom header(auth)
     */
    private fun newRequestWithAccessToken(
        request: Request,
        newToken: String
    ): Request {

        val req = request.newBuilder()

        if (userPreferences.isLogin()) {

            req.header("auth", newToken)
        }

        return req.build()
    }

AccessTokenInterceptor:

    /**
 * a Interceptor class for handel or add *Authorization* header into *okhttp* request after get response
 * implement from Interceptor (okhttp3.Interceptor) and KoinComponent (org.koin.core.KoinComponent)
 */
class AccessTokenInterceptor(
    private val userPreferences: UserPreferences
) : Interceptor {

    /**
     * override function for handle or add *Authorization* into *okhttp* request after get response
     *
     * @param chain intercept's chain <Interceptor.Chain>
     *
     * @return Response
     */
    override fun intercept(chain: Interceptor.Chain): Response {


        /*  if (userPreferences.isLogin()) {
            if (response.code() == HttpURLConnection.HTTP_UNAUTHORIZED) {

                response = getToken(chain, userPreferences.token)
            }*/

        return when (userPreferences.isLogin()) {

            true -> {

                val request = newRequestWithAccessToken(chain.request(), userPreferences.token)
                chain.proceed(request)
            }
            else -> {

                val request = newRequestWithoutAccessToken(chain.request())
                chain.proceed(request)
            }
        }

    }


    /**
     * create synchronized Api call for get device token and add into header and add into devicePreferences
     *
     * @param chain intercept's chain <Interceptor.Chain>
     *
     * @return a Response with custom header(Device-Id, Device-Token)
     */
 /*   private fun getToken(chain: Interceptor.Chain, previousToken: String): Response {

        synchronized(this) {

            val refreshTokenWithPreviousToken = chain.request().newBuilder()

            refreshTokenWithPreviousToken.header("auth", previousToken)

            val newAccessToken =
                userApi.checkToken().execute().body()


            if (userPreferences.token != newAccessToken?.apiObjects?.user?.token) {
                if (newAccessToken?.apiObjects?.user?.token?.isNotEmpty() == true) {
                    userPreferences.token = newAccessToken.apiObjects.user.token
                }

            }

            return chain.proceed(
                newRequestWithAccessToken(
                    refreshTokenWithPreviousToken.build(),
                    newAccessToken?.apiObjects?.user?.token ?: ""
                )
            )

        }
    }*/

    /**
     * create request with custom header(Device-Id, Device-Token)
     *
     * @param request request for add header <Request>
     * @param accessToken for add Device-Token header provided from server <String>
     *
     * @return a Request with custom header(Device-Id, Device-Token)
     */
    private fun newRequestWithAccessToken(
        request: Request,
        accessToken: String
    ): Request {

        val req = request.newBuilder()

        if (userPreferences.isLogin()) {

            req.header("auth", accessToken)
        }

        return req.build()
    }


    private fun newRequestWithoutAccessToken(
        request: Request
    ): Request {

        val req = request.newBuilder()

        return req.build()
    }

} 

RefreshTokenInterceptor:

 /**
 * a Interceptor class for handel or add *Authorization* header into *okhttp* request after get response
 * implement from Interceptor (okhttp3.Interceptor) and KoinComponent (org.koin.core.KoinComponent)
 */
class RefreshTokenInterceptor(
    private val checkTokenApi: CheckTokenApi,
    private val userPreferences: UserPreferences
) : Interceptor {

    /**
     * override function for handle or add *Authorization* into *okhttp* request after get response
     *
     * @param chain intercept's chain <Interceptor.Chain>
     *
     * @return Response
     */
    override fun intercept(chain: Interceptor.Chain): Response {

        var response: Response

        response = when (userPreferences.isLogin()) {

            true -> {

                val request = newRequestWithAccessToken(chain.request(), userPreferences.token)
                chain.proceed(request)
            }
            else -> {

                val request = newRequestWithoutAccessToken(chain.request())
                chain.proceed(request)
            }
        }

        if (userPreferences.isLogin()) {
            if (response.code() == HttpURLConnection.HTTP_UNAUTHORIZED) {

                response = getToken(chain, userPreferences.token)
            }
        }



        return response
    }


    /**
     * create synchronized Api call for get device token and add into header and add into devicePreferences
     *
     * @param chain intercept's chain <Interceptor.Chain>
     *
     * @return a Response with custom header(Device-Id, Device-Token)
     */
    private fun getToken(chain: Interceptor.Chain, previousToken: String): Response {

        synchronized(this) {

            val refreshTokenWithPreviousToken = chain.request().newBuilder()

            refreshTokenWithPreviousToken.header("auth", previousToken)

            val newAccessToken =
                checkTokenApi.checkToken().execute().body()


            if (userPreferences.token != newAccessToken?.apiObjects?.user?.token) {
                if (newAccessToken?.apiObjects?.user?.token?.isNotEmpty() == true) {
                    userPreferences.token = newAccessToken.apiObjects.user.token
                }

            }

            return chain.proceed(
                newRequestWithAccessToken(
                    refreshTokenWithPreviousToken.build(),
                    newAccessToken?.apiObjects?.user?.token ?: ""
                )
            )

        }
    }

    /**
     * create request with custom header(Device-Id, Device-Token)
     *
     * @param request request for add header <Request>
     * @param accessToken for add Device-Token header provided from server <String>
     *
     * @return a Request with custom header(Device-Id, Device-Token)
     */
    private fun newRequestWithAccessToken(
        request: Request,
        accessToken: String
    ): Request {

        val req = request.newBuilder()

        if (userPreferences.isLogin()) {

            req.header("auth", accessToken)
        }

        return req.build()
    }


    private fun newRequestWithoutAccessToken(
        request: Request
    ): Request {

        val req = request.newBuilder()

        return req.build()
    }

}

我不知道为什么会出现这个问题,但是这个 end-point 我尝试调用需要身份验证 header 和令牌。但是对于另一个 end-point 没有任何类似的登录和......一切都正确。请指导我发现问题。谢谢

我更改了此 class,现在一切正常:

AccessTokenAuthenticator:

/**
 * a Authenticator class for add *Authorization* header into *okhttp* request
 * implement from Authenticator (okhttp3.Authenticator) and KoinComponent (org.koin.core.KoinComponent)
 */
class AccessTokenAuthenticator(
    private val checkTokenApi: CheckTokenApi,
    private val userPreferences: UserPreferences
) : Authenticator {

    /**
     * override function for handle add *Authorization* into *okhttp* request
     *
     * @param route
     * @param response
     *
     * @return Request
     */
    @Nullable
    override fun authenticate(route: Route?, response: Response): Request? {

        if (response.code() == 401) {
            try {
                val sendCall = checkTokenApi.refreshToken()
                val refreshResult = sendCall.execute()
                if (refreshResult.isSuccessful) {
                    //save Token
                    userPreferences.token = refreshResult.body()?.apiObjects?.user?.token ?: ""

                    //Replace Token
                    return response.request().newBuilder()
                        .header(
                            "auth",
                            refreshResult.body()?.apiObjects?.user?.token ?: ""
                        )
                        .build()
                }
            } catch (ex: Exception) {
                // todo :: handle error
                println(ex)
            }

        }
        return null
    }
}

AccessTokenInterceptor:

/**
 * a Interceptor class for handel or add *Authorization* header into *okhttp* request after get response
 * implement from Interceptor (okhttp3.Interceptor) and KoinComponent (org.koin.core.KoinComponent)
 */
class AccessTokenInterceptor(
    private val userPreferences: UserPreferences
) : Interceptor {

    /**
     * override function for handle or add *Authorization* into *okhttp* request after get response
     *
     * @param chain intercept's chain <Interceptor.Chain>
     *
     * @return Response
     */
    override fun intercept(chain: Interceptor.Chain): Response {

        val request = chain.request()
        val newRequest: Request

        newRequest = request.newBuilder()
            .addHeader("auth", userPreferences.token)
            .build()
        return chain.proceed(newRequest)
    }
}

RefreshTokenInterceptor:

/**
 * a Interceptor class for handel or add *Authorization* header into *okhttp* request after get response
 * implement from Interceptor (okhttp3.Interceptor) and KoinComponent (org.koin.core.KoinComponent)
 */
class RefreshTokenInterceptor(
    private val userPreferences: UserPreferences
) : Interceptor {


    /**
     * override function for handle or add *Authorization* into *okhttp* request after get response
     *
     * @param chain intercept's chain <Interceptor.Chain>
     *
     * @return Response
     */
    override fun intercept(chain: Interceptor.Chain): Response {

        val request = chain.request()
        val newRequest: Request

        newRequest = request.newBuilder()
            .addHeader("auth", userPreferences.token)
            .build()
        return chain.proceed(newRequest)
    }
}

而且我认为我们可以只删除 RefreshTokenInterceptor 并只使用 AccessTokenInterceptor。我希望这可以帮助别人。