gpt4 book ai didi

android - 使用 koin 注入(inject) UseCase 时,方法在 android 中引发 'java.lang.StackOverflowError' 异常

转载 作者:行者123 更新时间:2023-12-03 20:53:08 28 4
gpt4 key购买 nike

当我尝试在域包中的 repositoryImpl 中注入(inject)这一行时:

 private val userUseCase: UserUseCase by inject()

我收到了这个错误:
java.lang.StackOverflowError: stack size 1040KB

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

我认为是因为在 DI 和 Koin 的提供者中实现不好。这是我的网络提供商和网络模块:

网络提供商:
    /**
* 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()
}

AccessToken拦截器:
    /**
* 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()
}

}

刷新 token 拦截器:
 /**
* 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()
}

}

我不知道为什么会出现这个问题,但是我尝试调用这个端点需要 auth header 和 token 。但是对于另一个没有任何类似登录的端点......一切都正确。请指导我发现一个问题。谢谢你

最佳答案

我改变了这个类,现在一切正常:

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
}
}

AccessToken拦截器:
/**
* 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)
}
}

刷新 token 拦截器:
/**
* 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。我希望这对其他人有帮助。

关于android - 使用 koin 注入(inject) UseCase 时,方法在 android 中引发 'java.lang.StackOverflowError' 异常,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/61983056/

28 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com