Hilt:提供一个在初始化之前需要异步操作的接口实例



我使用firebase id令牌作为http请求的授权头。要从android客户端获得这个令牌需要一个方法:user.getIdToken(true),这是一个基于异步函数的回调。我还有一个柄模块,它提供了一个改装服务(接口)的实例:

服务接口如下:

interface CountriesService {
@GET("countries")
suspend fun getCountries(): Response<CountriesResponse>
}

下面是提供此服务的Hilt模块:

@Module
@InstallIn(SingletonComponent::class)
object CountriesModule {
private const val CLOUD_RUN_BASE_URL = "https://my-base-url/"
@Provides
fun provideCountriesService(): CountriesService {
return Retrofit.Builder().apply {
baseUrl(CLOUD_RUN_BASE_URL)
addConverterFactory(GsonConverterFactory.create())
}.build().create(CountriesService::class.java)
}
}

现在我需要添加一个OkHttp检测器(简单地将授权头添加到所有请求)到上述模块,如:拦截器:

var client: OkHttpClient = OkHttpClient.Builder().addInterceptor { chain ->
val user = FirebaseAuth.getInstance().currentUser
user?.getIdToken(true)?.addOnCompleteListener { task ->
if(task.isSuccessful){
val token  = task.result.token
val newRequest: Request = chain.request().newBuilder()
.addHeader("Authorization", "Bearer $token")
.build()
chain.proceed(newRequest) // return not allowed here
} else{
Log.d("id_token", task.exception?.message.toString())
}
}

}.build()

然后将client值作为参数传递给下面模块中的client()方法:

@Module
@InstallIn(SingletonComponent::class)
object CountriesModule {
private const val CLOUD_RUN_BASE_URL = "https://my-base-url/"
@Provides
fun provideCountriesService(): CountriesService {
return Retrofit.Builder().apply {
client(client) // Newly added line
baseUrl(CLOUD_RUN_BASE_URL)
addConverterFactory(GsonConverterFactory.create())
}.build().create(CountriesService::class.java)

}

}.

拦截器代码的问题是不能从addOnCompleteListener{}块返回值chain.proceed(newRequest)

有更好的方法来实现这一点吗?如何使一个基于回调的方法返回一个稍后被另一个方法使用的值?

您必须重新考虑初始化并初始化HTTP客户端,并将依赖于它的所有代码移到应用程序的后面阶段。在Android上,只有一组有限的事情可以在初始化回调中同步进行。

通常情况下,应用程序在初始化阶段启动时只显示一个启动屏幕,此时需要网络。

您需要重新考虑如何进行调用,特别是调用的顺序。可能在拦截器中应该使用缓存的令牌,但在此之前,需要使令牌失效。这将导致你的拦截器总是使用最新的令牌。

或者,你可以坚持firebaseTask提供结果阻塞,你的HTTP调用总是在后台线程,但我不建议。

最新更新