2、Retrofit基础回顾:注解、接口定义、OkHttp集成
好,咱们正式开始第二讲。这一节我会把 Retrofit 最核心的三个东西串一遍:注解体系、接口定义方式、以及它怎么跟 OkHttp 勾搭上的。你可能会觉得这些是基础,但说实话,很多 Mock 方案的坑都出在对这三个部分的误解上。先扎扎实实过一遍,后面再讲拦截和回放,你会感觉特别顺。
2.1 注解:Retrofit 的灵魂
Retrofit 本质上就是个注解处理器。你写一个接口,加上几个注解,它就能自动帮你把 HTTP 请求拼好。我自己刚开始用的时候也觉得挺神奇——“这不就是动态代理嘛?” 对,但注解才是真正驱动它工作的 DNA。
注解一共分三类,我按使用频率给大家排个序:
| 分类 | 常用注解 | 作用 |
|---|---|---|
| 方法注解 | @GET、@POST、@PUT、@DELETE |
指定 HTTP 方法和请求路径 |
| 参数注解 | @Query、@Path、@Body、@Header |
绑定请求参数、路径变量、请求体或头部 |
| 标记注解 | @FormUrlEncoded、@Multipart、@Streaming |
声明请求的编码格式或特殊行为 |
举个例子,咱们定义一个获取用户信息的接口:
public interface UserService {
@GET("users/{id}")
Call<User> getUser(@Path("id") int userId);
}
上面这段代码,Retrofit 会怎么处理?它会解析 @GET("users/{id}"),然后把 {id} 替换成你传入的 userId,最终发起一个 GET 请求到 /users/123。这是最标准的用法。
String BASE_USERS = "users/{id}"。这样做的好处是避免硬编码散落在各个调用点,后面做 Mock 匹配路由时也方便统一管理。
2.2 接口定义:不仅仅是写个 interface
Retrofit 的接口定义,说白了就是你跟后端的一次“合约”声明。我在项目中见过不少人把接口写得很随意,结果后期维护像噩梦。来,看看规范的写法应该长什么样。
先看返回类型。Retrofit 支持好几种:
Call<T>—— 最经典,支持同步/异步切换Response<T>—— 直接拿到完整响应,包含状态码、头部、bodyObservable<T>/Single<T>—— 配合 RxJava 做响应式suspend fun—— Kotlin 协程时代的首选
我个人现在在 Kotlin 项目里几乎只用协程,代码简洁太多了:
interface UserService {
@GET("users/{id}")
suspend fun getUser(@Path("id") int userId): User
}
写接口的时候,还有两个容易忽视的点:
- 泛型要精确:不要为了省事写成
Call<Object>,后面反序列化会出问题。 - 接口粒度要适中:一个接口别塞太多方法,我习惯按业务模块拆分,比如
UserService、OrderService、PaymentService。这样测试的时候可以单独 Mock,隔离性好。
Call<ResponseBody> 的接口用在文件下载上,结果忘了加 @Streaming 注解。小文件没事,一遇到大文件直接 OOM。原因很简单——Retrofit 默认会把整个 response body 读进内存。如果你要做二进制数据流的 Mock,一定要记住 @Streaming 这个标记。
2.3 OkHttp 集成:Retrofit 的底层引擎
有一种常见的误解,觉得 Retrofit 是独立工作的。其实它只是一个“翻译层”,真正的网络请求是交给 OkHttp 执行的。你想想看,Retrofit 本身没有连接池、没有拦截器链、没有超时控制,这些全是 OkHttp 的活儿。
在构建 Retrofit 实例的时候,我们通常会传入一个自定义的 OkHttpClient:
OkHttpClient client = new OkHttpClient.Builder()
.connectTimeout(10, TimeUnit.SECONDS)
.readTimeout(30, TimeUnit.SECONDS)
.addInterceptor(new LoggingInterceptor())
.build();
Retrofit retrofit = new Retrofit.Builder()
.baseUrl("https://api.example.com/")
.client(client)
.addConverterFactory(GsonConverterFactory.create())
.build();
看到这里没?.client(client) 这一行就是集成的关键。Retrofit 拿到接口定义后,会生成一个 OkHttpCall 对象,这个对象内部直接调用了 OkHttp 的 RealCall 去执行网络请求。换句话说,你在 OkHttp 上加的所有拦截器,都会作用到 Retrofit 发出的每个请求上。
对我来说,这就是实现 Mock 回放的“手术刀口”。咱们后面的课程里,AddInterceptor 会是我们插入 Mock 逻辑的核心位置。你只需要在拦截器里判断:是走真实请求,还是走本地录制好的数据。
收个尾,我整理了一张图,把这三者的关系画出来了。你看完应该会清晰很多:
这张图里其实已经暗示了咱们后面的 Mock 回放要怎么做。你看,拦截器链就是那扇门——你可以选择让它走真实的网络,也可以把本地录好的数据直接塞进去。Retrofit 上层完全感知不到差异。
这一章就到这。你只需要记住三个层次:注解定义接口、Retrofit 生成代理、OkHttp 执行请求。后面每一节都会回来反复引用这个结构。