实现OkHTTP Authenticator时,如何将高阶函数回调转为返回值?
如何将OkHttp Authenticator中的异步回调转为同步返回值?
问题场景
尝试通过高阶函数实现OkHttp的Authenticator接口,期望在authenticate方法中从provideToken的回调里获取新token,但实际返回时token始终为null。已知可以直接在authenticate里执行同步API调用,但想了解是否有办法将异步回调转换为同步返回值。
问题代码
import okhttp3.Authenticator import okhttp3.Request import okhttp3.Response import okhttp3.Route class TokenExpirationImplementation( private val tokenExpirationHandler: TokenExpirationHandler ): Authenticator { override fun authenticate(route: Route?, response: Response): Request? { var token : String? = null tokenExpirationHandler.provideToken { providedToken -> token = providedToken } return response.request.newBuilder() .removeHeader(AUTH_HEADER) .addHeader(AUTH_HEADER, token) .build() } }
原因分析
当前代码返回null的核心原因是provideToken是异步执行的:authenticate是同步方法,在回调函数{ providedToken -> token = providedToken }执行前,就已经完成了return语句,此时token还没被赋值,自然是null。
解决方案
要将异步回调转为同步返回,本质是需要阻塞当前线程,等待回调执行完成。Kotlin中有两种常用实现方式:
方式1:使用协程CompletableDeferred + runBlocking
利用协程的CompletableDeferred包装回调结果,通过runBlocking阻塞线程等待结果返回:
import okhttp3.Authenticator import okhttp3.Request import okhttp3.Response import okhttp3.Route import kotlinx.coroutines.CompletableDeferred import kotlinx.coroutines.runBlocking class TokenExpirationImplementation( private val tokenExpirationHandler: TokenExpirationHandler ): Authenticator { override fun authenticate(route: Route?, response: Response): Request? { val tokenDeferred = CompletableDeferred<String?>() tokenExpirationHandler.provideToken { providedToken -> tokenDeferred.complete(providedToken) } // 阻塞线程,等待回调传递token val token = runBlocking { tokenDeferred.await() } return token?.let { response.request.newBuilder() .removeHeader(AUTH_HEADER) .addHeader(AUTH_HEADER, it) .build() } } }
方式2:使用CountDownLatch(无协程场景)
如果项目未使用协程,可通过Java并发工具CountDownLatch实现阻塞等待:
import okhttp3.Authenticator import okhttp3.Request import okhttp3.Response import okhttp3.Route import java.util.concurrent.CountDownLatch import java.util.concurrent.TimeUnit class TokenExpirationImplementation( private val tokenExpirationHandler: TokenExpirationHandler ): Authenticator { override fun authenticate(route: Route?, response: Response): Request? { var token: String? = null val latch = CountDownLatch(1) tokenExpirationHandler.provideToken { providedToken -> token = providedToken latch.countDown() // 回调执行完成,释放等待 } // 设置超时时间(比如5秒),避免无限阻塞 latch.await(5, TimeUnit.SECONDS) return token?.let { response.request.newBuilder() .removeHeader(AUTH_HEADER) .addHeader(AUTH_HEADER, it) .build() } } }
注意事项
- 线程阻塞风险:
runBlocking或latch.await()会阻塞当前线程,如果provideToken内部是耗时的异步操作(比如网络请求),可能会拖慢OkHttp的请求流程;如果在Android主线程执行,还可能引发ANR。 - 回调可靠性:必须确保
provideToken的回调一定会执行(无论成功还是失败),否则线程会一直阻塞。建议添加超时时间,避免无限等待。 - 设计合理性:OkHttp的
Authenticator本身是同步设计,若token刷新逻辑本身是异步的,更优的方案是提前预刷新token(比如在token即将过期时主动刷新),而非在authenticate里阻塞等待。
内容的提问来源于stack exchange,提问作者Adrian Coman
相关产品推荐
相关产品推荐

