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读取,避免硬编码在代码里(防止密钥泄露):- 在
local.properties添加:MOVIE_DB_API_KEY=你的密钥 - 在
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")}\"") } } - 代码中通过
BuildConfig.MOVIE_DB_API_KEY获取密钥。
- 在
内容的提问来源于stack exchange,提问作者Saneen K P
相关产品推荐
相关产品推荐

