第一个 GET 请求:从定义接口到真正干活

好,咱们终于要发起第一个真实的网络请求了。前面两章讲了 Retrofit 的定位和配置,说白了都是热身。这一章,我带你把一个 GET 请求从零跑到通。

我个人习惯是:先把接口定义写好,再配 Retrofit 实例,最后调用。这三步走完,你的第一个数据就拉回来了。

1. 定义接口:告诉 Retrofit 要干什么

接口是 Retrofit 的“约定”。你用 @GET 注解声明一个方法,Retrofit 就会自动帮你拼出 URL、组装请求。

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

注意几点:

  • @GET 里写的是 相对路径,不要写 URL。比如说 "users/{user}/repos" 就对了。
  • 花括号 {user} 是占位符,后面用 @Path 动态填值。
  • 返回值必须是 Call<T> 或者 Retrofit 2.6+ 支持的 suspend 协程函数。咱们这一章先用 Call。
小经验:我遇到过团队有人把 @GET 的路径写成了 https://api.github.com/users/{user}/repos,结果运行时一直报错。千万别把 baseUrl 拼到注解里!

2. 创建 Retrofit 实例:配好“中间人”

接口定义好了,但 Retrofit 还不知道你要用哪个 baseUrl、用什么解析器。所以得先造一个 Retrofit 对象。

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

关键字段意思:

配置项作用
baseUrl所有请求的根地址,必须以 / 结尾,否则运行时可能出奇怪的路径拼接问题。
addConverterFactory告诉 Retrofit 怎么把 JSON 转成 Java 对象。最常用的是 Gson,也可以用 Moshi、Jackson。
build()造出不可变的 Retrofit 实例。
我栽过的坑:baseUrl 末尾忘记加斜杠,结果接口路径拼接成 https://api.github.comusers/xxx —— 少了一个 /,服务器直接 404。从那以后我每次写完都检查末尾分隔符。

3. 发起同步/异步调用:数据到手

有了 Retrofit 实例,就能生成接口的实现对象:

GitHubService service = retrofit.create(GitHubService.class);
Call<List<Repo>> call = service.listRepos("octocat");

现在 call 只是一个“请求描述”,还没真正发出去。怎么发?两种方式:

同步调用(卡线程)

用 execute() 方法。它会阻塞当前线程直到拿到响应。适合后台线程(比如 RxJava 调度器或协程),不能在主线程调。

new Thread(() -> {
    try {
        Response<List<Repo>> response = call.execute();
        if (response.isSuccessful()) {
            List<Repo> repos = response.body();
            // 处理数据
        }
    } catch (IOException e) {
        e.printStackTrace();
    }
}).start();

同步调用的返回值是 Response<T>,你可以拿到 HTTP 状态码、headers、body。我一般用 response.isSuccessful() 判断 2xx 状态码。

异步调用(非阻塞)

用 enqueue() 方法。Retrofit 内部会切换到后台线程执行请求,结果通过回调返回。

call.enqueue(new Callback<List<Repo>>() {
    @Override
    public void onResponse(Call<List<Repo>> call, Response<List<Repo>> response) {
        if (response.isSuccessful()) {
            List<Repo> repos = response.body();
            // UI 线程更新视图
        }
    }

    @Override
    public void onFailure(Call<List<Repo>> call, Throwable t) {
        // 网络异常、解析失败等
    }
});

注意了:onResponse 和 onFailure 默认在 主线程 执行,所以你可以在里面直接更新 UI。这是 Retrofit 体贴的地方——省去了手动切线程的麻烦。

我的建议:多数 App 用异步就足够了,没必要自己搞线程池。除非你需要精细控制并发,或者做批量请求。

完整流程一条龙

为了让你看得更清楚,我画了张流程图。从定义接口到拿到数据,就这几步。

第一个GET请求完整流程 第一个GET请求:三步走流程 ① 定义接口 @GET + Call<T> ② 创建Retrofit baseUrl + Converter ③ 发起请求 execute / enqueue execute() 同步 阻塞线程,返回Response enqueue() 异步 回调在主线程执行 处理结果:解析 body / 处理错误 response.isSuccessful() → response.body() / onFailure() 注意:同步调用不能在主线程,异步回调默认在主线程

需要特别留意的地方

  • Call 只能执行一次:无论是 execute 还是 enqueue,每个 Call 对象只能用一次。如果你想重复请求,得通过 clone() 复制一个新 Call。我曾遇到过复用同一个 Call 导致“already executed”异常,还挺恼火的。
  • 关于 baseUrl 的斜杠:再说一遍,baseUrl 末尾一定要加 /,不然 Retrofit 会把接口的相对路径当作路径来拼接。这是 Retrofit 的设计哲学——它不会自作主张帮你补斜杠。
  • Response 不等于成功:onResponse 回调里拿到的是 HTTP 响应,不代表业务成功。404、500 也会走 onResponse,但 response.isSuccessful() 为 false。只有网络异常、超时、解析失败才会走 onFailure。
调试小技巧:在创建 Retrofit 实例时加上 .addInterceptor(new HttpLoggingInterceptor().setLevel(HttpLoggingInterceptor.Level.BODY)),就能在 Logcat 里看到完整的请求 URL、请求头和响应体。我每次写新接口都会先开这个,看一眼 URL 拼对了没。

代码跑通了,然后呢?

这一章的内容其实已经够你用 Retrofit 发起第一个真实请求了。定义接口、创建实例、调用——三部曲很清晰。以后你会碰到更多注解,比如 @Query、@Body、@Header,但核心套路不变。

嗯,说实话,我第一次用 Retrofit 时觉得“怎么比 OkHttp 多那么多步骤”,但用顺手后真香——代码干净、测试方便、类型安全。希望你也尽快进入“真香”状态。