在android中,当用koin注入UseCase时,方法引发了'java.lang.StackOverflowError'异常。

问题描述 投票:2回答:1

当我试图在domain包的repositoryImpl中注入这一行时。

 private val userUseCase: UserUseCase by inject()

我得到了这个错误。

java.lang.StackOverflowError: stack size 1040KB

我的包结构是:- data -> for retrofit, room, and ...- domain -> repository implement here and make connection between data and presentation- presentation -> for UI。

我想是因为DI和Koin的Provider实现的不好,这是我的网络供应商和网络模块。

NetworkProvider:

    /**
 * 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)

NetworkModule:

    /**
 * 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()
    }

}

我不知道为什么会发生这个问题,但这个端点我试图调用需要认证头和令牌,但对于另一个端点,没有任何像登录和......一切正确。

android retrofit stack-overflow interceptor koin
1个回答
1
投票

我修改了这个类,现在一切都正常了。

AccessTokenAuthenticator: AccessTokenInterceptor:

/**
 * 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。希望对别人有所帮助。

© www.soinside.com 2019 - 2024. All rights reserved.