You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.18 02:45:38