不使用Dagger等依赖注入如何解决OkHttp authenticator与Retrofit ApiService循环依赖问题
无依赖注入框架下的Token刷新循环依赖解决方案
核心思路
循环依赖的根源是你用了同一个Retrofit实例同时生成普通业务接口和刷新令牌接口的ApiService,我们直接拆分出独立的无认证逻辑的Retrofit实例专门用于调用刷新令牌接口,从根源上切断循环依赖。
具体实现步骤
1. 改造TokenAuthenticator类
首先给TokenAuthenticator的构造函数传入你需要的Jetpack Datastore实例,同时传入专门用于刷新令牌的ApiService实例:
class TokenAuthenticator( private val dataStore: DataStore<Preferences>, private val refreshTokenApiService: ApiService ) : Authenticator { override fun authenticate(route: Route?, response: Response): Request? { // 1. 从Datastore取出旧的刷新令牌 val oldRefreshToken = runBlocking { dataStore.data.first()[yourRefreshTokenKey] } ?: return null // 没有刷新令牌直接返回,走登录流程 // 2. 调用刷新令牌接口(用专门的refreshTokenApiService,不会触发authenticator逻辑) val refreshResponse = runBlocking { runCatching { refreshTokenApiService.refreshToken(oldRefreshToken) }.getOrNull() } ?: return null // 刷新失败返回,走登录流程 // 3. 把新的令牌、刷新令牌存入Datastore runBlocking { dataStore.edit { it[yourAccessTokenKey] = refreshResponse.accessToken it[yourRefreshTokenKey] = refreshResponse.refreshToken } } // 4. 用新的令牌重新构建请求返回 return response.request.newBuilder() .header("Authorization", "Bearer ${refreshResponse.accessToken}") .build() } }
注意:你可以把刷新令牌的接口单独抽成独立的Service接口,也可以直接复用原来的ApiService,只要用独立的Retrofit构建即可
2. 改造RetrofitBuilder,拆分两个Retrofit实例
object RetrofitBuilder { private const val BASE_URL = "..." // 先构建专门用于刷新令牌的Retrofit,不添加TokenAuthenticator private fun buildRefreshTokenRetrofit(): Retrofit { val loggingInterceptor = HttpLoggingInterceptor() .setLevel(HttpLoggingInterceptor.Level.BODY) val okHttpClient = OkHttpClient.Builder() // 这里不要加TokenAuthenticator,避免循环 .addInterceptor(loggingInterceptor) .build() return Retrofit.Builder() .client(okHttpClient) .baseUrl(BASE_URL) .addConverterFactory(GsonConverterFactory.create()) .build() } // 对外提供的业务ApiService构建方法 operator fun invoke( networkConnectionInterceptor: NetworkConnectionInterceptor, dataStore: DataStore<Preferences> ) : ApiService { // 先拿到刷新令牌用的ApiService实例 val refreshApiService = buildRefreshTokenRetrofit().create(ApiService::class.java) // 实例化TokenAuthenticator,传入需要的Datastore和刷新用的ApiService val tokenAuthenticator = TokenAuthenticator(dataStore, refreshApiService) val loggingInterceptor = HttpLoggingInterceptor() .setLevel(HttpLoggingInterceptor.Level.BODY) val okHttpClient = OkHttpClient.Builder() .authenticator(tokenAuthenticator) .addInterceptor(networkConnectionInterceptor) .addInterceptor(loggingInterceptor) .build() return Retrofit.Builder() .client(okHttpClient) .baseUrl(BASE_URL) .addConverterFactory(GsonConverterFactory.create()) .build() .create(ApiService::class.java) } }
可选轻量方案:懒加载后置赋值
如果你不想拆分两个Retrofit实例,也可以通过懒加载+后置设置依赖的方式解决:
- 给TokenAuthenticator增加可空的ApiService变量,或者用
lateinit var声明ApiService属性 - 构建OkHttpClient的时候先传入未赋值ApiService的TokenAuthenticator实例
- 等Retrofit构建出ApiService之后,再把ApiService赋值给TokenAuthenticator的对应属性
该方案实现更简单,但要注意多线程并发场景下的空安全问题,更推荐拆分Retrofit的方案
内容的提问来源于stack exchange,提问作者Sagar Ghare
相关产品推荐
相关产品推荐

