Android中Kotlin协程+Retrofit调用Spotify API请求顺序问题
问题解决:Spotify API 请求顺序错误导致空 Token
你的核心问题是**getSpotifyToken 函数虽然标记为 suspend,但内部用了 Retrofit 的 enqueue 异步回调,函数会直接返回,不会等待 Token 获取完成**,导致后续的 getAlbums 请求在 Token 赋值前就发起,传入了空值。
解决方案:让 Token 获取逻辑真正挂起
需要改造 getSpotifyToken,让它在 Token 回调完成前挂起协程,确保后续代码拿到有效 Token 再执行。有两种常用方式:
方式1:使用 Retrofit 协程扩展的 await()
先确保你的项目引入了 kotlinx-coroutines-retrofit 依赖,然后直接用 await() 替代 enqueue,将异步回调转为挂起函数:
private suspend fun getSpotifyToken(networkMusicRepository: NetworkMusicRepository): String { val response = networkMusicRepository.getToken().await() return if (response.isSuccessful) { val token = response.body()?.accessToken ?: throw IOException("Token 响应体为空") NetworkUiState.token = token token } else { throw HttpException(response) } }
然后调整调用逻辑,直接获取返回的 Token 传入 getAlbums:
viewModelScope.launch { networkUiState = try { val networkMusicRepository = NetworkMusicRepository() // 等待 Token 获取完成后再继续 val token = getSpotifyToken(networkMusicRepository) val listResult = networkMusicRepository.getAlbums(token) NetworkUiState.Success } catch (e: IOException) { Log.d("RESPONSE", e.message.toString()) NetworkUiState.Error } catch (e: HttpException) { Log.d("RESPONSE", e.response()?.errorBody()?.string().toString()) NetworkUiState.Error } }
方式2:用 suspendCoroutine 封装回调
如果无法使用 Retrofit 协程扩展,可以手动用 suspendCoroutine 将回调转为挂起函数:
private suspend fun getSpotifyToken(networkMusicRepository: NetworkMusicRepository): String { return suspendCoroutine { continuation -> val responseCall = networkMusicRepository.getToken() responseCall.enqueue(object : Callback<TokenResponse> { override fun onResponse(call: Call<TokenResponse>, response: Response<TokenResponse>) { val responseBody = response.body() if (responseBody != null) { val token = responseBody.accessToken Log.i("RESPONSE", token) NetworkUiState.token = token // 回调完成,恢复协程并返回 Token continuation.resume(token) } else { continuation.resumeWithException(IOException("Token 响应体为空")) } } override fun onFailure(call: Call<TokenResponse>, t: Throwable) { // 请求失败,抛出异常 continuation.resumeWithException(t) } }) } }
调用逻辑和方式1一致,确保 getAlbums 在 Token 拿到后执行。
关键原理
标记为 suspend 的函数本身不会自动挂起,只有内部调用了其他挂起函数(比如 await()、suspendCoroutine)时,才会真正暂停协程执行,直到异步任务完成。你原来的代码里用 enqueue 是纯异步回调,不会阻塞协程,所以后续代码会立刻执行。
内容的提问来源于stack exchange,提问作者Max
相关产品推荐
相关产品推荐

