25、网络请求之Retrofit:注解、动态代理、Converter、与RxJava结合

说实话,Retrofit 是我在 Android 项目里用得最多的网络库。没有之一。

为什么?因为它够简单,够优雅。你只需要定义一个接口,加几个注解,网络请求就搞定了。底层它帮你封装了 OkHttp,上层又提供了和 RxJava、协程的无缝衔接。今天我们就把它拆开揉碎了讲清楚。

25.1 Retrofit 的核心思想:用注解定义接口

Retrofit 的核心思路,说白了就是:你只管定义“要什么”,它负责“怎么要”

你写一个 Java 接口,方法上加上 @GET@POST 这些注解,Retrofit 就自动帮你生成实现类,发起网络请求,解析返回数据。

核心三要素:

  • 注解:描述请求方式、路径、参数、头信息
  • 动态代理:运行时生成接口的实现
  • Converter:把 JSON 转成 Java 对象

我个人习惯把 API 接口定义放在一个单独的 api 包下,每个模块一个接口文件。这样后期维护起来,你想想看,是不是一目了然?

25.2 常用注解详解

Retrofit 的注解分两类:请求方法注解参数注解

请求方法注解

注解 说明 示例
@GET GET 请求 @GET("users/list")
@POST POST 请求 @POST("users/create")
@PUT PUT 请求 @PUT("users/update")
@DELETE DELETE 请求 @DELETE("users/delete")
@HEAD HEAD 请求 @HEAD("users/check")

参数注解

注解 说明 示例
@Path 替换 URL 路径中的占位符 @Path("id") int userId
@Query 添加查询参数 @Query("page") int page
@QueryMap 多个查询参数(Map) @QueryMap Map<String, String> options
@Body 请求体(自动序列化) @Body User user
@Header 单个请求头 @Header("Authorization") String token
@Headers 多个请求头 @Headers({"Accept: application/json"})
// 一个完整的接口定义示例
public interface ApiService {
    @GET("users/{id}")
    Call<User> getUser(@Path("id") int userId);

    @POST("users/create")
    Call<User> createUser(@Body User user);

    @GET("users/list")
    Call<List<User>> listUsers(@Query("page") int page, @Query("size") int size);
}

我的经验: 路径中的占位符尽量用语义化的名字,比如 {userId} 而不是 {id}。我在项目中遇到过好几次,因为占位符名字太泛,导致多人协作时搞混了参数。

25.3 动态代理:Retrofit 的魔法

你有没有想过,为什么只写一个接口,Retrofit 就能发起网络请求?

答案就是 动态代理

Retrofit 在运行时,通过 Proxy.newProxyInstance() 为你的接口生成一个代理对象。当你调用接口方法时,代理对象会拦截调用,解析方法上的注解,构建出一个 OkHttpRequest 对象,然后发起请求。

// 动态代理的核心逻辑(简化版)
public <T> T create(final Class<T> service) {
    return (T) Proxy.newProxyInstance(
        service.getClassLoader(),
        new Class<?>[] { service },
        new InvocationHandler() {
            @Override
            public Object invoke(Object proxy, Method method, Object[] args) {
                // 解析方法注解,构建请求
                ServiceMethod<?> serviceMethod = loadServiceMethod(method);
                OkHttpCall<?> okHttpCall = new OkHttpCall<>(serviceMethod, args);
                return serviceMethod.adapt(okHttpCall);
            }
        }
    );
}

说白了,动态代理就是 Retrofit 的“发动机”。没有它,注解就只是一堆标记,毫无意义。

注意: 动态代理只能代理接口,不能代理类。所以你的 API 定义必须是 interface,不能是 class。我曾经见过有人写成抽象类,结果运行时报错,排查了半天。

25.4 Converter:数据转换的桥梁

网络请求返回的通常是 JSON 或 XML 字符串。但我们的代码里需要的是 Java 对象。谁来做这个转换?

就是 Converter

Retrofit 本身不负责序列化/反序列化,它只定义了 Converter 接口。你可以选择 Gson、Jackson、Moshi 等任意库来实现。

// 添加 Gson 转换器
Retrofit retrofit = new Retrofit.Builder()
    .baseUrl("https://api.example.com/")
    .addConverterFactory(GsonConverterFactory.create())
    .build();

// 添加 Jackson 转换器
Retrofit retrofit = new Retrofit.Builder()
    .baseUrl("https://api.example.com/")
    .addConverterFactory(JacksonConverterFactory.create())
    .build();

我的建议: 团队项目里统一用一个序列化库。我见过一个项目里 Gson 和 Jackson 混用,结果日期格式不统一,排查问题花了一整天。统一用 Gson 吧,社区支持好,坑也少。

25.5 与 RxJava 结合:响应式编程

Retrofit 默认返回 Call<T> 对象。但如果你用了 RxJava,就可以返回 Observable<T>Single<T>Flowable<T> 等。

这样做的好处是什么?

  • 链式调用:可以用 .map().flatMap() 做数据转换
  • 线程切换.subscribeOn(Schedulers.io()).observeOn(AndroidSchedulers.mainThread())
  • 错误处理.onErrorResumeNext().retry() 等操作符
// 添加 RxJava 适配器
Retrofit retrofit = new Retrofit.Builder()
    .baseUrl("https://api.example.com/")
    .addConverterFactory(GsonConverterFactory.create())
    .addCallAdapterFactory(RxJava3CallAdapterFactory.create())
    .build();

// 接口定义
public interface ApiService {
    @GET("users/{id}")
    Single<User> getUser(@Path("id") int userId);
}

// 调用
apiService.getUser(123)
    .subscribeOn(Schedulers.io())
    .observeOn(AndroidSchedulers.mainThread())
    .subscribe(user -> {
        // 更新 UI
    }, throwable -> {
        // 处理错误
    });

避坑指南: 我曾经在 .subscribe() 里忘记处理 Disposable,导致 Activity 销毁后网络回调还在执行,引发了内存泄漏。记得用 CompositeDisposable 统一管理,在 onDestroy() 里调用 clear()

25.6 知识体系总览

下面这张图,帮你把 Retrofit 的核心脉络理清楚:

Retrofit 注解 @GET, @POST, @Path... 动态代理 Proxy.newProxyInstance Converter Gson, Jackson, Moshi RxJava 结合 Observable, Single 工作流程: 1. 定义接口 + 注解 → 描述请求 2. 动态代理 → 运行时生成实现 3. Converter → JSON ↔ Java 对象 4. RxJava → 异步 + 响应式编程

25.7 完整示例:从定义到调用

最后,我们串起来跑一个完整的例子。假设我们要获取用户列表:

// 1. 定义接口
public interface UserApi {
    @GET("users")
    Single<List<User>> getUsers(@Query("page") int page);
}

// 2. 构建 Retrofit 实例
Retrofit retrofit = new Retrofit.Builder()
    .baseUrl("https://api.example.com/")
    .addConverterFactory(GsonConverterFactory.create())
    .addCallAdapterFactory(RxJava3CallAdapterFactory.create())
    .build();

// 3. 创建接口实例
UserApi userApi = retrofit.create(UserApi.class);

// 4. 发起请求
CompositeDisposable disposable = new CompositeDisposable();
disposable.add(
    userApi.getUsers(1)
        .subscribeOn(Schedulers.io())
        .observeOn(AndroidSchedulers.mainThread())
        .subscribe(users -> {
            // 更新 RecyclerView
            adapter.setData(users);
        }, error -> {
            // 显示错误提示
            Toast.makeText(context, "加载失败", Toast.LENGTH_SHORT).show();
        })
);

// 5. 别忘了在 onDestroy 中清理
@Override
protected void onDestroy() {
    super.onDestroy();
    disposable.clear();
}

重要提醒: baseUrl 必须以 / 结尾。否则 Retrofit 会抛出 IllegalArgumentException。我刚开始用的时候踩过这个坑,排查了半小时才发现是少了个斜杠。

好了,Retrofit 的核心内容就这些。注解定义请求,动态代理生成实现,Converter 做数据转换,RxJava 做异步处理。这四个点吃透了,Retrofit 你就掌握了八成。

剩下的两成,是在实战中慢慢积累的。比如统一错误处理、添加拦截器、配合协程使用等等。那些我们后面再聊。


公众号:蓝海资料掘金营,微信deep3321