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

OkHttp+Retrofit生成URL异常:缺失BASE_URL路径段致404错误

问题原因与解决方案

1. URL缺失/3/的核心原因

Retrofit中,当@GET注解的路径以/开头时,会完全覆盖baseUrl中的路径部分。你的baseUrl是https://api.themoviedb.org/3/,但@GET("/movie/now_playing")的绝对路径写法会直接忽略/3/,导致最终URL丢失版本号段。

修复方法

把接口中的@GET路径改为相对路径(去掉开头的/):

interface MoviesApiServiceRx {
    @GET("movie/now_playing")
    fun getNowPlayingMovies(): Single<List<MovieData>>
}

2. 代码优化建议

你的代码存在初始化顺序错误、空指针风险、冗余逻辑等问题,以下是优化后的完整代码及说明:

优化点说明

  • 避免初始化顺序错误:原代码在init中先初始化moviesApiServiceRx,但该方法依赖未初始化的requestInterceptor,会触发空指针。改为使用lazy委托延迟初始化,确保依赖项就绪后再创建实例。
  • 消除空检查冗余:Kotlin中用lazy或object单例替代手动空判断,更简洁安全。
  • 简化单例实现:将MoviesRetrofitClient改为单例类,避免重复创建Retrofit实例。
  • 规范变量命名:修正变量命名为驼峰式,符合Kotlin编码规范。

优化后完整代码

import io.reactivex.Single
import okhttp3.Interceptor
import okhttp3.OkHttpClient
import okhttp3.logging.HttpLoggingInterceptor
import retrofit2.Retrofit
import retrofit2.adapter.rxjava2.RxJava2CallAdapterFactory
import retrofit2.converter.gson.GsonConverterFactory
import java.util.concurrent.TimeUnit

interface MoviesApiServiceRx {
    @GET("movie/now_playing")
    fun getNowPlayingMovies(): Single<List<MovieData>>
}

// 单例实现,确保全局只有一个Retrofit客户端实例
object MoviesRetrofitClient {
    private const val BASE_URL = "https://api.themoviedb.org/3/"
    private const val API_KEY = "9a976526fce8c29aaa35eb4a1e654d3c"

    // 延迟初始化拦截器
    private val requestInterceptor: Interceptor by lazy {
        Interceptor { chain ->
            val newUrl = chain.request()
                .url
                .newBuilder()
                .addQueryParameter("api_key", API_KEY)
                .build()

            val newRequest = chain.request()
                .newBuilder()
                .url(newUrl)
                .build()

            chain.proceed(newRequest)
        }
    }

    // 延迟初始化Gson转换器
    private val gsonConverterFactory: GsonConverterFactory by lazy {
        GsonConverterFactory.create()
    }

    // 延迟初始化OkHttpClient
    private val okHttpClient: OkHttpClient by lazy {
        val httpLoggingInterceptor = HttpLoggingInterceptor().apply {
            level = HttpLoggingInterceptor.Level.BODY
        }

        OkHttpClient.Builder()
            .addInterceptor(requestInterceptor)
            .addInterceptor(httpLoggingInterceptor)
            .connectTimeout(60, TimeUnit.SECONDS)
            .build()
    }

    // 延迟初始化API服务
    private val moviesApiServiceRx: MoviesApiServiceRx by lazy {
        Retrofit.Builder()
            .baseUrl(BASE_URL)
            .client(okHttpClient)
            .addConverterFactory(gsonConverterFactory)
            .addCallAdapterFactory(RxJava2CallAdapterFactory.create())
            .build()
            .create(MoviesApiServiceRx::class.java)
    }

    fun getNowPlayingMovies(): Single<List<MovieData>> {
        return moviesApiServiceRx.getNowPlayingMovies()
    }
}

额外提示

  • 建议将API_KEY放在local.properties文件中,通过BuildConfig读取,避免硬编码在代码里(防止密钥泄露):
    1. 在local.properties添加:MOVIE_DB_API_KEY=你的密钥
    2. 在app/build.gradle的android块中添加:
      buildTypes {
          release {
              // ...其他配置
              buildConfigField("String", "MOVIE_DB_API_KEY", "\"${properties.getProperty("MOVIE_DB_API_KEY")}\"")
          }
          debug {
              buildConfigField("String", "MOVIE_DB_API_KEY", "\"${properties.getProperty("MOVIE_DB_API_KEY")}\"")
          }
      }
      
    3. 代码中通过BuildConfig.MOVIE_DB_API_KEY获取密钥。

内容的提问来源于stack exchange,提问作者Saneen K P

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.20 07:09:23