第2章 Retrofit基础接口定义:定义API接口方法、配置BaseUrl、使用注解描述请求
Retrofit 的核心价值在于将 HTTP API 声明为 Java 或 Kotlin 接口,通过注解直接描述请求的路径、参数、方法等,从而让网络层与业务逻辑解耦。本章将详细讲解如何定义接口方法、配置 BaseUrl 以及使用关键注解,为后续引入 WorkManager 做后台同步打下基础。
2.1 接口定义原理
Retrofit 在运行时通过动态代理将接口方法调用转换为 HTTP 请求。开发者只需提供一个普通接口(interface),在方法上添加注解(如 @GET、@POST),Retrofit 就会根据这些信息自动拼接 URL、填充参数、解析返回结果。整个过程无需开发者手动创建 HttpURLConnection 或管理线程池。
原理要点: 接口方法的方法名、参数、注解共同构成一个完整的 HTTP 请求描述;Retrofit 负责将其翻译为 OkHttp 的 Request 对象。
2.2 核心注解说明
Retrofit 提供了丰富的注解用于描述请求。下表列出最常用的注解及其作用:
| 注解 | 作用 | 示例 | 备注 |
|---|---|---|---|
@GET |
发送 GET 请求 | @GET("users") |
路径可包含路径参数占位符 {} |
@POST |
发送 POST 请求 | @POST("users") |
配合 @Body 发送请求体 |
@Path |
替换 URL 中的占位符 | @Path("id") int id |
必须有对应的 {} 占位符 |
@Query |
添加查询参数,如 ?page=1 | @Query("page") int page |
自动拼接在 URL 末尾 |
@Body |
设置请求体(非表单) | @Body User user |
默认转为 JSON(需配合 Converter) |
@Header |
添加请求头 | @Header("Authorization") |
可动态传入值 |
@FormUrlEncoded |
表示表单编码的 POST 请求 | 类级别注解 | 配合 @Field 使用 |
注意: 注解中的路径不能以 '/' 开头(除非你希望忽略 BaseUrl 的路径部分),且 BaseUrl 必须以 '/' 结尾。否则会导致 URL 拼接异常。
2.3 定义接口方法
以下是一个完整的接口定义示例,演示不同注解的组合:
// Kotlin 示例(Java 语法类似)
interface GitHubApi {
@GET("users")
suspend fun getUsers(): List
@GET("users/{id}")
suspend fun getUserById(@Path("id") userId: Long): User
@POST("users")
suspend fun createUser(@Body user: User): User
@GET("search/users")
suspend fun searchUsers(
@Query("q") query: String,
@Query("per_page") perPage: Int = 20
): SearchResult
@HEAD("users/{id}")
suspend fun headUser(@Path("id") userId: Long): Long // 返回 Content-Length
}
关键点:
- 使用
suspend关键字支持协程(Kotlin 推荐方式),Java 则返回Call<T>。 - 路径参数
{id}必须与@Path("id")中的名称一致。 @Query参数可为空,Retrofit 会忽略 null 值。
2.4 配置 BaseUrl
BaseUrl 是接口所有请求的基础地址,通常在创建 Retrofit 实例时设置。标准做法:
val retrofit = Retrofit.Builder()
.baseUrl("https://api.github.com/") // 必须以 '/' 结尾
.addConverterFactory(GsonConverterFactory.create())
.build()
val gitHubApi = retrofit.create(GitHubApi::class.java)
最佳实践: 将 BaseUrl 定义在全局常量中(如 BuildConfig.API_BASE_URL),便于切换测试/生产环境。同时确保末尾是 '/',否则 Retrofit 会抛出 IllegalArgumentException。
2.5 完整示例:接口定义 + 调用
实际项目中,通常会结合对象转换器一起使用。下面是一个完整的可运行片段:
// 1. 数据类
data class User(val id: Long, val login: String, val avatarUrl: String)
// 2. 接口
interface GitHubApi {
@GET("users")
suspend fun getUsers(): List<User>
}
// 3. 构建 Retrofit
private const val BASE_URL = "https://api.github.com/"
fun createApi(): GitHubApi {
return Retrofit.Builder()
.baseUrl(BASE_URL)
.addConverterFactory(GsonConverterFactory.create())
.build()
.create(GitHubApi::class.java)
}
// 4. 使用协程调用
suspend fun loadUsers(): List<User> {
val api = createApi()
return api.getUsers()
}
2.6 调试与常见错误
- BaseUrl 尾部缺少 '/': Retrofit 启动时会抛出
IllegalArgumentException: baseUrl must end with /。 - 路径注解写死 '/' 开头: 如
@GET("/users"),Retrofit 会忽略 BaseUrl 的路径部分,只保留 Scheme + Host,导致端口或路径丢失。 - 缺少 ConverterFactory: 如果接口返回非
ResponseBody或String,必须添加对应的 Converter(如 Gson、Moshi),否则运行时抛出IllegalArgumentException: ... Unable to create converter for ...。 - 调试方法: 在 OkHttpClient 中加入 HttpLoggingInterceptor 即可打印完整请求 URL 和参数。配置代码:
val logging = HttpLoggingInterceptor().apply {
level = HttpLoggingInterceptor.Level.BASIC // 或 HEADERS / BODY
}
val client = OkHttpClient.Builder()
.addInterceptor(logging)
.build()
val retrofit = Retrofit.Builder()
.baseUrl(BASE_URL)
.client(client) // 注入自定义 client
.addConverterFactory(GsonConverterFactory.create())
.build()
稳定性优化: 可为 OkHttpClient 设置超时时间,避免接口定义返回慢时永久阻塞:
.connectTimeout(30, TimeUnit.SECONDS).readTimeout(30, TimeUnit.SECONDS)