第4章 接口定义规范:核心注解与返回类型设计
Retrofit 的接口定义是网络层模块的核心契约。它通过声明式注解将 HTTP 方法、URL 路径、请求参数、请求体以及返回类型映射到 Java 方法上。本章将详细拆解 @GET/@POST/@PUT/@DELETE 注解的语义、参数注解 @Query/@Path/@Body 的使用场景,以及返回类型的设计原则,并给出标准代码模板与常见错误规避方案。
4.1 注解-方法映射原理
Retrofit 在运行时通过动态代理解析接口方法上的注解,自动构建 okhttp3.Request 对象。其核心映射关系如下图所示:
4.2 方法注解对比与使用规范
| 注解 | HTTP 方法 | 典型 URL 示例 | 语义说明 |
|---|---|---|---|
@GET | GET | /api/users?page=1 | 获取资源,幂等、可缓存 |
@POST | POST | /api/users | 创建资源,非幂等,通常带 Body |
@PUT | PUT | /api/users/42 | 全量替换资源,幂等 |
@DELETE | DELETE | /api/users/42 | 删除资源,幂等 |
经验原则:用
@GET 获取列表或单条;@POST 新增;@PUT 全量更新;@DELETE 删除。不要用 @GET 传递请求体,OkHttp 会直接忽略。
4.3 参数注解详解与代码示例
参数注解决定了请求中的变量如何填充。以下是最常用的三种注解:
4.3.1 @Path:路径占位符替换
用于替换 URL 模板中的 {...} 占位符。注意:基本类型与 String 可直接传入;自定义对象需提供 toString() 或使用 @Path(encoded = true) 避免自动编码。
// 接口定义
@GET("users/{id}/posts")
Call<List<Post>> getUserPosts(@Path("id") int userId);
// 调用示例(Retrofit 会自动替换 42 → /users/42/posts)
ApiService api = retrofit.create(ApiService.class);
Call<List<Post>> call = api.getUserPosts(42);
4.3.2 @Query:查询参数拼接
追加到 URL 的 ? 之后,支持多次出现或数组/List 自动展开(用 @Query(encoded = true) 控制是否预编码)。
// 接口定义
@GET("search")
Call<SearchResult> search(
@Query("q") String query,
@Query("page") int page,
@Query("size") int size
);
// 实际请求:GET /search?q=Retrofit&page=1&size=20
4.3.3 @Body:请求体填充
将 Java 对象自动序列化为 JSON(需配合 GsonConverterFactory 或 MoshiConverterFactory)。POST/PUT 必须使用 @Body,GET 不应使用。
// 接口定义
@POST("users")
Call<User> createUser(@Body User user);
// 调用示例
User newUser = new User("alice", "alice@example.com");
Call<User> call = api.createUser(newUser);
// 请求体:{"name":"alice","email":"alice@example.com"}
常见错误:多个
@Body 不能并存;@Body 不能和 @Field(表单)混用;若实体字段为 null,转换器默认会跳过该字段,可能导致服务端参数缺失,推荐在实体类上用 @Nullable 或定制适配器。
4.4 返回类型设计
Retrofit 支持多种返回类型,选择取决于解耦需求:
| 返回类型 | 特点 | 适用场景 |
|---|---|---|
Call<T> | 同步/异步二选一,可取消 | 标准 REST 请求,推荐作为默认选择 |
Response<T> | 包裹原始 HTTP 响应,可获取状态码、Headers | 需要检查 HTTP 状态码、重定向或自定义错误处理 |
Observable<T>(RxJava) | 响应式流,支持线程切换、组合操作 | 复杂异步编排,多请求合并 |
CompletableFuture<T>(Java 8+) | Future 风格,可等待或回调 | Java 协程或线程池场景 |
自定义 CallAdapter | 完全自定义返回类型(如 Kotlin suspend) | 框架集成(如 LiveData、Flow) |
推荐最低使用 Call<T>,这样保持通用性且易于单元测试。若需要访问 HTTP 错误码,可改用 Call<Response<T>>。
// 推荐声明方式
public interface ApiService {
@GET("users/{id}")
Call<User> getUser(@Path("id") int id);
// 当需要 HTTP 状态码时
@GET("users/{id}")
Call<Response<User>> getUserWithResponse(@Path("id") int id);
}
4.5 常见错误与性能稳定优化
- URL 模板不匹配:
@Path中的名称必须与{...}占位符完全一致,否则运行时抛出IllegalArgumentException。 - 忽略查询参数编码:当值含特殊字符(如中文、空格)时,应使用
@Query(encoded = false)(默认)让 Retrofit 自动 URLEncode;若已预编码则设为true。 - 返回类型为
void或Void:虽然允许,但失去错误处理能力。除非客户端完全不在乎响应,否则应返回Call<Void>。 - 性能优化:为常用接口预编译
ServiceMethod缓存(Retrofit 2.6+ 已自动缓存);避免在循环中动态创建Call对象。 - 调试技巧:添加
HttpLoggingInterceptor可打印完整请求/响应体,方便验证注解效果。
调试命令示例(Logcat 过滤 Retrofit 日志):
或在
adb logcat -s Retrofit:V OkHttp:V或在
AndroidManifest 中加 android:debuggable="true" 后在运行时开启日志。