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

如何在Kotlin中使用Retrofit2获取响应URL(Spotify授权场景)

在Android Kotlin中处理Spotify授权码流程的回调参数(suspend版)

核心说明

Spotify授权码流程中,用户授权后的回调参数是通过App的Deep Link/Scheme带回你的应用,而非Retrofit发起的HTTP请求响应。所以你不需要通过Retrofit获取响应URL,直接从Android的Intent中解析回调URI的查询参数即可。以下是具体实现步骤:


1. 配置App的Deep Link回调

首先在AndroidManifest.xml中为处理回调的Activity添加Intent Filter,匹配你在Spotify开发者后台配置的回调URI:

<activity
    android:name=".SpotifyAuthCallbackActivity"
    android:launchMode="singleTask">
    <intent-filter>
        <action android:name="android.intent.action.VIEW" />
        <category android:name="android.intent.category.DEFAULT" />
        <category android:name="android.intent.category.BROWSABLE" />
        <!-- 替换成你自己的回调URI Scheme -->
        <data
            android:scheme="yourapp"
            android:host="spotify-auth"
            android:path="/callback" />
    </intent-filter>
</activity>

2. 在回调Activity中解析参数(suspend函数实现)

在回调Activity中,从Intent中提取回调URI,解析出code、state等关键参数,再用suspend函数处理后续的Token请求:

class SpotifyAuthCallbackActivity : AppCompatActivity() {
    // 初始化Retrofit的Spotify API实例
    private val spotifyApi by lazy {
        Retrofit.Builder()
            .baseUrl("https://accounts.spotify.com/")
            .addConverterFactory(GsonConverterFactory.create())
            .build()
            .create(SpotifyApi::class.java)
    }

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        lifecycleScope.launch {
            intent.data?.let { uri ->
                handleAuthCallback(uri)
            }
            finish()
        }
    }

    override fun onNewIntent(intent: Intent?) {
        super.onNewIntent(intent)
        intent?.data?.let { uri ->
            lifecycleScope.launch {
                handleAuthCallback(uri)
            }
        }
        finish()
    }

    private suspend fun handleAuthCallback(callbackUri: Uri) {
        // 解析查询参数
        val authCode = callbackUri.getQueryParameter("code")
        val state = callbackUri.getQueryParameter("state")
        val error = callbackUri.getQueryParameter("error")

        when {
            error != null -> {
                // 处理授权错误,比如用户拒绝授权
                Log.e("SpotifyAuth", "Authorization failed: $error")
            }
            authCode != null -> {
                // 用授权码请求Access Token
                runCatching {
                    val tokenResponse = spotifyApi.fetchAccessToken(
                        code = authCode,
                        redirectUri = "yourapp://spotify-auth/callback",
                        clientId = "YOUR_SPOTIFY_CLIENT_ID",
                        clientSecret = "YOUR_SPOTIFY_CLIENT_SECRET"
                    )
                    // 保存Token到本地,比如DataStore或SharedPreferences
                    saveTokenToLocal(tokenResponse)
                }.onFailure {
                    Log.e("SpotifyAuth", "Failed to fetch token", it)
                }
            }
        }
    }

    private suspend fun saveTokenToLocal(tokenResponse: TokenResponse) {
        // 实现Token持久化逻辑
    }
}

3. Retrofit接口定义(suspend版)

定义获取Access Token的接口,使用suspend函数,无需Call<>或.enqueue():

interface SpotifyApi {
    @FormUrlEncoded
    @POST("api/token")
    suspend fun fetchAccessToken(
        @Field("grant_type") grantType: String = "authorization_code",
        @Field("code") code: String,
        @Field("redirect_uri") redirectUri: String,
        @Field("client_id") clientId: String,
        @Field("client_secret") clientSecret: String
    ): TokenResponse
}

data class TokenResponse(
    @SerializedName("access_token") val accessToken: String,
    @SerializedName("token_type") val tokenType: String,
    @SerializedName("expires_in") val expiresIn: Long,
    @SerializedName("refresh_token") val refreshToken: String
)

特殊场景:用Retrofit获取重定向URL

如果你确实需要通过Retrofit获取授权请求的重定向URL(比如自动处理跳转),可以通过Response.raw()获取OkHttp的原始响应,进而拿到最终URL:

interface SpotifyAuthRedirectApi {
    @GET("authorize")
    suspend fun getAuthRedirect(
        @Query("client_id") clientId: String,
        @Query("response_type") responseType: String = "code",
        @Query("redirect_uri") redirectUri: String,
        @Query("scope") scope: String,
        @Query("state") state: String
    ): Response<Void>
}

// 调用示例
suspend fun getRedirectUrl(): String? {
    val response = spotifyAuthRedirectApi.getAuthRedirect(
        clientId = "YOUR_CLIENT_ID",
        redirectUri = "yourapp://spotify-auth/callback",
        scope = "user-read-private user-read-email",
        state = "random_unique_state"
    )
    return if (response.isRedirect) {
        response.raw().request.url.toString()
    } else {
        null
    }
}

注意:这种方式仅适用于无用户交互的重定向场景,Spotify的授权页面需要用户手动登录授权,所以更推荐用WebView/Custom Tab加载授权URL,再通过Deep Link回调处理参数。

内容的提问来源于stack exchange,提问作者RetroGhost

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.25 22:24:56