Retrofit-OkHttp Authenticator:如何让其他API请求等待认证完成
解决并行API请求时重复刷新Token的问题
当前在ViewModel中通过viewModelScope并行发起三个API请求,当请求返回401时,Authenticator会触发三次Token刷新操作。需要实现:当第一个请求触发Token刷新时,其余两个请求暂停等待,直到刷新完成后再继续执行,且不能将所有请求放在同一个launch块中。
核心解决方案
通过全局同步Token刷新操作,确保同一时间只有一个刷新请求在执行,所有需要刷新Token的请求共享这一次刷新的结果。
1. 实现全局同步的Token管理类
创建单例的Token管理类,用Mutex保证刷新操作的互斥性,同时缓存正在进行的刷新任务,避免重复发起请求:
class TokenManager { private val refreshMutex = Mutex() private var pendingRefresh: Deferred<String?>? = null // 使用IO线程Scope执行刷新操作,避免阻塞主线程 private val refreshScope = CoroutineScope(Dispatchers.IO + SupervisorJob()) suspend fun refreshTokenSafely(): String? { return refreshMutex.withLock { // 若已有正在进行的刷新任务,直接等待其结果 pendingRefresh?.await() ?: run { // 发起新的刷新请求并缓存任务 val deferred = refreshScope.async { performRefreshTokenRequest() } pendingRefresh = deferred val result = deferred.await() // 刷新完成后清空缓存任务 pendingRefresh = null result } } } private suspend fun performRefreshTokenRequest(): String? { // 这里替换为你实际的刷新Token API调用逻辑 // 示例:return apiService.refreshToken().body()?.token } } // 全局单例实例 val tokenManager = TokenManager()
2. 修改Authenticator使用同步刷新方法
在Authenticator中调用同步刷新方法,避免重复发起刷新请求:
override fun authenticate(route: Route?, response: Response): Request? { return if (response.retryCount < 1) { // 用runBlocking调用挂起函数,内部刷新操作已在IO线程执行,不会阻塞主线程 val newToken = runBlocking { tokenManager.refreshTokenSafely() } newToken?.let { response.request.newBuilder() .header("Authorization", "Bearer $it") .build() } ?: run { navigateToOnboardingActivity() null } } else { navigateToOnboardingActivity() null } }
3. ViewModel请求代码保持不变
原有的并行请求逻辑无需修改,同步逻辑已在网络层处理:
viewModelScope.launch { apiCallOne() } viewModelScope.launch { apiCallTwo() } viewModelScope.launch { apiCallThree() }
关键原理说明
Mutex确保同一时间只有一个协程能进入刷新逻辑块,彻底避免并发刷新。pendingRefresh缓存正在进行的刷新任务,后续401请求直接等待该任务结果,无需重复发起刷新。- 刷新操作绑定IO线程Scope,不会阻塞主线程,保证界面流畅性。
内容的提问来源于stack exchange,提问作者Harish Padmanabh
相关产品推荐
相关产品推荐

