第一章 Retrofit简介与核心概念

老话重提:Retrofit到底是什么?

这几年面试的时候,我发现很多新人一上来就说“Retrofit是个网络请求框架”。嗯……对,但不全对。
我个人的理解更偏底层一点:Retrofit本质上是OkHttp的封装器,它的核心工作是把你用注解写的Java接口,转化成一个个OkHttp的Call对象。
说白了,它只负责把“方法调用”翻译成“网络请求配置”,真正发请求和接响应的脏活累活,全交给OkHttp去干。

你可能要问:那我自己用OkHttp不就行了?为什么还要套一层Retrofit?
举个例子,我早期的一个项目直接拼OkHttp的Request.Builder,每个接口都要手动写URL、Header、Body。后来接口从30个涨到200个,代码里全是重复的模板,改一个公共Header要翻好几个文件。用Retrofit之后,每个接口只需要一个方法注解,参数、路径全在接口声明里,维护成本直接降了一个量级。


1.1 Retrofit是什么

定义:Retrofit是一个类型安全的REST客户端,由Square公司开源。它通过Java注解来描述HTTP请求,并将接口定义自动转换为可执行请求。

一句话总结:你写接口,Retrofit帮你干活——生成代理、拼接URL、序列化参数、发起请求、反序列化响应。

我在实际项目中用它做过一个有意思的事:给一个物联网平台写SDK,需要同时支持JSON和Protocol Buffers两种数据格式。Retrofit的Converter机制让我只改了3行配置就完成了切换——这种灵活性是裸用OkHttp给不了的。

1.2 核心组件

我们一起拆一下Retrofit的“五脏六腑”。记住这四块,后面讲拦截器和缓存就好理解了。

组件 职责 个人经验
Retrofit 实例 配置基URL、Converter、CallAdapter等,生成接口代理对象 我习惯用单例管理Retrofit实例,避免重复创建
接口定义(Interface) 用注解声明HTTP方法、路径、参数、Header 注意路径参数不要拼错,我吃过@Path("id")写反的亏
OkHttpClient 真正的网络通信层,负责连接、读写、拦截器链 一定要复用同一个OkHttpClient,否则缓存和连接池不生效
Converter 将Java对象转为请求体,将响应体转为Java对象 GsonConverterFactory最常见,但Kotlin时代我更喜欢Moshi
CallAdapter 将Call对象转换为其他类型(如RxJava的Observable、Kotlin协程的Deferred) 这个组件常被忽略,但它是实现异步和回调的关键
小提醒:核心组件里还有Call和Callback。不过现在协程流行,用suspend方法后连Callback都省了。我这几年新项目直接上协程,接口返回类型直接写ResponseBody或自定义Bean,清爽得很。

1.3 工作流程

Retrofit的工作流程,其实就五步:

  1. 定义接口:用注解写好每个API的细节
  2. 创建Retrofit实例:配置baseUrl、OkHttpClient、Converter等
  3. 生成代理对象:调用retrofit.create(ApiService.class),Retrofit使用Java动态代理生成实现类
  4. 构建请求:当调用接口方法时,Retrofit根据注解和方法参数构建一个OkHttp Request对象
  5. 执行请求:交给OkHttpClient去真正发送请求,得到响应后通过Converter反序列化返回

我把这个流程画成一张图,方便大家一目了然:

Retrofit 工作流程 接口定义(@GET等) 动态代理生成 构建Request对象 OkHttpClient.send() Converter反序列化 关键点 • 接口方法不能有返回值类型 • 动态代理在首次create时生成 • 每个call都是新的OkHttp请求 • 转换器决定返回数据类型

你可能注意到了,中间少了一步——拦截器在哪里?
嗯,拦截器在OkHttpClient.send()这一步介入。你可以把它理解成水管上的阀门,每个请求发出前和响应回来后,都会经过拦截器链。
我们后面会花好几章专门讲这个,别着急。

1.4 一个最小可运行示例

光说不练假把式,咱们写个最简单的Retrofit接口:

public interface GitHubApi {
    @GET("users/{user}/repos")
    Call<List<Repo>> listRepos(@Path("user") String user);
}

// 创建Retrofit实例
Retrofit retrofit = new Retrofit.Builder()
    .baseUrl("https://api.github.com/")
    .addConverterFactory(GsonConverterFactory.create())
    .build();

GitHubApi api = retrofit.create(GitHubApi.class);
Call<List<Repo>> call = api.listRepos("square");
call.enqueue(new Callback<List<Repo>>() {
    @Override
    public void onResponse(Call<List<Repo>> call, Response<List<Repo>> response) {
        // 处理响应
    }
    @Override
    public void onFailure(Call<List<Repo>> call, Throwable t) {
        // 处理失败
    }
});
我曾经踩过的坑:baseUrl必须以/结尾,否则Retrofit会报IllegalArgumentException。如果接口路径以/开头,则baseUrl的结尾斜杠会被忽略——这一点在官方文档里写得很隐晦,我是调试了半小时才发现的。

1.5 核心概念速查表

概念 说明 常见误解
@GET / @POST / @PUT / @DELETE HTTP方法注解,标注在接口方法上 有人以为可以混用,实际一个方法只能有一种
@Path 替换URL中的占位符 忘记编码特殊字符?@Path默认不做URL编码,得自己用@Url
@Query 添加查询参数 多个同名参数用@QueryMap
@Body 将对象序列化为请求体 只能在POST/PUT中使用
@Header / @Headers 添加请求头 静态Header用@Headers,动态用@Header

这些注解看起来简单,但在和拦截器配合时会产生有趣的化学反应。比如我们在拦截器里添加统一的Authorization头部,再配合@Header的覆盖逻辑,就能实现动态认证的优雅方案——这是后面章节的重头戏,这里先留个念想。