3. Android 第三方框架 Okhttp, Retrofit 的动态代理和适配器模式深度解读三

摘要: Retrofit通过Java动态代理 在运行时生成接口的实现类。当调用声明了HTTP注解(如@GET)的接口方法时,代理拦截请求:

  1. 解析方法注解和参数,构建HTTP请求模板;
  2. 将参数注入请求模板生成完整请求;
  3. 委托OkHttp执行网络调用。
    此机制实现声明式API,开发者只需定义接口,Retrofit+OkHttp自动处理网络通信,极大简化代码。

思维导图: 1.设计模式 2.线程池,队列 3.网络知识

1.完整流程分析(从代理创建到请求发起)

使用的过程

less 复制代码
// 原始接口方法
@GET("users/{user}/repos")
Call<List<Repo>> listRepos(@Path("user") String user);
swift 复制代码
// 1. 创建Retrofit实例
Retrofit retrofit = new Retrofit.Builder()
    .baseUrl("https://api.github.com/")
    .addConverterFactory(GsonConverterFactory.create())
    .build();

// 2. 创建动态代理对象
GitHubService service = retrofit.create(GitHubService.class);

// 3. 调用接口方法
Call<List<Repo>> call = service.listRepos("octocat");

// 4. 发起网络请求
call.enqueue(new Callback<List<Repo>>() {
    @Override
    public void onResponse(Call<List<Repo>> call, Response<List<Repo>> response) {
        // 处理成功响应
        List<Repo> repos = response.body();
    }
    
    @Override
    public void onFailure(Call<List<Repo>> call, Throwable t) {
        // 处理失败
    }
});

第一步骤: 创建Retrofit

第二步骤: 创建动态代理对象 GitHubService service = retrofit.create(GitHubService.class);

第三步骤: 代理的是接口调用方法: Call<List> call = service.listRepos("octocat");

第四步骤: 请求放入线程池执行

1.1.1 根本的作用:核心目标:将Java接口方法转换为HTTP请求,无需手动实现接口。

1.1.2 create()方法: 返回的是一个接口!

ini 复制代码
UserService service = retrofit.create(UserService.class);

create() 返回的是一个动态生成的接口代理对象,该对象:

  1. 在方法调用时实时解析注解
  2. 封装网络请求为可执行对象
  3. 通过适配器支持多种返回类型
  4. 本身不包含状态,仅作为访问 Retrofit 核心功能的入口

1.1.3 方法拦截机制

当调用接口方法时,实际执行的是 InvocationHandler.invoke()

typescript 复制代码
    new InvocationHandler() {
    @Override 
    public Object invoke(Object proxy, Method method, Object[] args) {
        // 1. 解析方法注解
        ServiceMethod<?> serviceMethod = loadServiceMethod(method);
        // 2. 创建网络请求对象
        OkHttpCall<?> okHttpCall = new OkHttpCall<>(serviceMethod, args);
        // 3. 适配返回类型
        return serviceMethod.adapt(okHttpCall);
    }
}

方法调用流程图

1.2 代理对象返回的实际类型

实际返回的是ServiceMethod.adapt()的结果 return serviceMethod.adapt(okHttpCall);

less 复制代码
  
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) {
                // 核心逻辑:解析方法并执行请求:cite[1]:cite[3]
                ServiceMethod serviceMethod = loadServiceMethod(method);
                OkHttpCall okHttpCall = new OkHttpCall(serviceMethod, args);
                return serviceMethod.adapt(okHttpCall);
            }
        });
}


整个T,就是返回的T
public interface GitHubService {
    // T = Call<List<Repo>>
    @GET("users/{user}/repos")
    Call<List<Repo>> listRepos(@Path("user") String user);
    
    // T = Observable<List<Repo>>
    @GET("users/{user}/repos")
    Observable<List<Repo>> listReposRx(@Path("user") String user);
    
    // T = Deferred<List<Repo>>
    @GET("users/{user}/repos")
    Deferred<List<Repo>> listReposDeferred(@Path("user") String user);
}

中间过程: 1)。 注解解析 : 2)。参数处理 : 3)。适配器与转换器

最终封装的是一个call对象! T,是实体对象

1.2.1 拦截方法调用(InvocationHandler.invoke()

当调用接口方法时,触发以下流程:

  1. 解析方法元数据 :通过loadServiceMethod(method)加载或创建ServiceMethod对象,解析方法注解(如@GET)、参数注解(如@Path)及返回值类型。
  2. 构造OkHttpCall :将解析后的参数封装为OkHttpCall,内部持有OkHttp请求配置。
  3. 适配返回值 :通过serviceMethod.adapt()OkHttpCall转换为目标类型(如Call<T>Observable<T>

1.2.2 ServiceMethod: 核心类

ini 复制代码
ServiceMethod<?> loadServiceMethod(Method method) {
    ServiceMethod<?> result = serviceMethodCache.get(method);
    if (result != null) return result;

    synchronized (serviceMethodCache) {
        // 1. 解析方法注解
        RequestFactory requestFactory = RequestFactory.parseAnnotations(retrofit, method);
        
        // 2. 解析返回类型
        Type returnType = method.getGenericReturnType();
        
        // 3. 获取CallAdapter
        CallAdapter<ResponseT, ReturnT> callAdapter = createCallAdapter(method);
        
        // 4. 获取Converter
        Converter<ResponseBody, ResponseT> responseConverter = createResponseConverter(method);
        
        // 5. 创建ServiceMethod
        result = new ServiceMethod<>(requestFactory, callAdapter, responseConverter);
        
        // 6. 缓存结果
        serviceMethodCache.put(method, result);
        return result;
    }
}
ServiceMethod的核心职责
  • 注解解析 :使用RequestFactory解析方法注解,生成HTTP请求模板(URL、方法类型等)。
  • 数据转换 :通过Converter.Factory将参数转换为HTTP请求体(如JSON序列化
  • 适配调度 :调用CallAdapter.adapt()OkHttpCall适配为接口声明的返回值类型

1.3 动态生成的代理类源码(模拟)

typescript 复制代码
// JVM 动态生成的代理类(伪代码)
public final class $Proxy0 extends Proxy implements GitHubService {
    private static Method m1;  // hashCode()
    private static Method m2;  // equals()
    private static Method m3;  // toString()
    private static Method m4;  // listRepos()
    
    static {
        try {
            m4 = Class.forName("com.example.GitHubService")
                      .getMethod("listRepos", String.class);
        } catch (Exception e) {
            throw new Error(e);
        }
    }
    
    public $Proxy0(InvocationHandler h) {
        super(h);
    }
    
    @Override
    public Call<List<Repo>> listRepos(String user) {
        try {
            // 所有调用转发给InvocationHandler
            return (Call<List<Repo>>) h.invoke(this, m4, new Object[]{user});
        } catch (RuntimeException | Error e) {
            throw e;
        } catch (Throwable t) {
            throw new UndeclaredThrowableException(t);
        }
    }
}    

问题:代理的是啥?生成的是啥?

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

// 代理前:纯接口,无实现
GitHubService service = ? // 无法直接实例化

// 代理后:生成实现类
GitHubService service = retrofit.create(GitHubService.class);
service.listRepos("octocat"); // 触发网络请求

1.4 适配器模式(CallAdapter)的联动

流程图:

1. 注册适配器工厂
scss 复制代码
Retrofit retrofit = new Retrofit.Builder()
    .addCallAdapterFactory(RxJava2CallAdapterFactory.create()) // 注册RxJava适配器
    .build();
2. 接口方法声明目标类型
less 复制代码
public interface GitHubService {
    @GET("users/{user}/repos")
    Observable<List<Repo>> listRepos(@Path("user") String user); // 返回Observable
}
3. 关键源码定位(简化版)
typescript 复制代码
// ServiceMethod 中完成适配
final class ServiceMethod<R> {
  CallAdapter<R, ?> callAdapter;
  
  Object adapt(Call<R> call) {
    return callAdapter.adapt(call); // 关键适配点
  }
}

// RxJavaCallAdapter 实现
final class RxJavaCallAdapter implements CallAdapter<R, Observable<?>> {
  @Override 
  public Observable<?> adapt(Call<R> call) {
    return Observable.create(emitter -> {
      call.enqueue(new Callback<R>() {
        @Override public void onResponse(Call<R> c, Response<R> response) {
          emitter.onNext(response.body());
          emitter.onComplete();
        }
        
        @Override public void onFailure(Call<R> c, Throwable t) {
          emitter.onError(t);
        }
      });
    });
  }
}

Retrofit 的适配器模式实现了"网络请求执行"与"结果处理方式"的分离: 我们需要什么结果类型,自动转换成什么样的数据类型!

有哪些适配器

  1. Kotlin 项目 → 首选 协程 suspend 函数(Retrofit 2.6.0+ 原生支持)
  2. 响应式项目RxJava (Android) / Reactor (Spring)
  3. Java 项目CompletableFuture (Java 8+) 或 RxJava
  4. 特殊需求 → 自定义适配器(如 OptionalResult 封装)

1.5 Okhttp和Retrofit的对比

有了Okhttp,为什么还要Retrofit,它是为了解决什么问题

  • 每个 API 都需要手动构建 Request 对象
  • 需要手动拼接 URL 和参数
  • 需要重复编写响应解析逻辑

从下面的使用对比: okhttp需要手动封装请求和处理json数据! Retrofit的动态代理和适配器模式就是解决了这2个问题!

1.5.1 okhttp的使用

scss 复制代码
private void okhttp(){
    // 1. 创建客户端
    OkHttpClient client = new OkHttpClient();

   // 2. 手动构建请求
    Request request = new Request.Builder()
            .url("https://api.github.com/users/octocat/repos")
            .build();

   // 3. 发送请求并处理原始响应
    client.newCall(request).enqueue(new Callback() {
        @Override
        public void onResponse(Call call, Response response) throws IOException {
            // 4. 手动解析 JSON
            String json = response.body().string();
            List<Repo> repos = new Gson().fromJson(json, new TypeToken<List<Repo>>(){}.getType());

            // 5. 手动线程切换
            runOnUiThread(() -> updateUI(repos));
        }

        @Override
        public void onFailure(Call call, IOException e) {
            // 错误处理
        }
    });
}

1.5.2 Retrofit的使用

less 复制代码
private void retrofit(){
    // 1. 定义接口(声明式API)
    interface GitHubService {
        @GET("users/{user}/repos")
        Call<List<Repo>> listRepos(@Path("user") String user);
    }

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

    GitHubService service = retrofit.create(GitHubService.class);

   // 3. 直接调用接口方法
    service.listRepos("octocat").enqueue(new Callback<List<Repo>>() {
        @Override
        public void onResponse(Call<List<Repo>> call, Response<List<Repo>> response) {
            // 4. 自动获得解析后的对象(主线程回调)
            updateUI(response.body());
        }

        @Override
        public void onFailure(Call<List<Repo>> call, Throwable t) {
            // 错误处理
        }
    });
}

两者关系如图:

整体的架构图

设计优势

  • 解耦:接口定义与实现分离
  • 复用:统一处理所有API方法

2. 如果不用动态代理,怎么实现retrofit的那部分

2.1、手动实现接口方案(静态代理)

java 复制代码
// 接口定义
public interface GitHubService {
    @GET("users/{user}/repos")
    Call<List<Repo>> listRepos(@Path("user") String user);
}

// 手动实现类
public class ManualGitHubService implements GitHubService {
    private final Retrofit retrofit;
    
    public ManualGitHubService(Retrofit retrofit) {
        this.retrofit = retrofit;
    }
    
    @Override
    public Call<List<Repo>> listRepos(String user) {
        // 1. 手动解析注解
        String path = "users/" + user + "/repos";
        
        // 2. 构建请求
        HttpUrl url = retrofit.baseUrl().resolve(path);
        Request request = new Request.Builder()
            .url(url)
            .get()
            .build();
        
        // 3. 创建OkHttpCall
        okhttp3.Call rawCall = retrofit.callFactory().newCall(request);
        
        // 4. 创建Converter
        Converter<ResponseBody, List<Repo>> converter = retrofit.responseBodyConverter(
            new TypeToken<List<Repo>>(){}.getType(),
            new Annotation[0]
        );
        
        // 5. 返回包装后的Call
        return new OkHttpCall<>(request, rawCall, converter);
    }
}

// 客户端使用
Retrofit retrofit = new Retrofit.Builder()
    .baseUrl("https://api.github.com/")
    .build();

GitHubService service = new ManualGitHubService(retrofit);
Call<List<Repo>> call = service.listRepos("octocat");
call.enqueue(...);

缺点:

  1. 每个接口都需要手动创建实现类
  2. 注解解析逻辑重复
  3. 新增接口方法需要修改实现类
  4. 无法自动处理复杂参数(如@QueryMap)
反射调用(无动态代理)
  • 原理:手动解析方法注解和参数,通过反射执行请求。
  • 优点:灵活性高;无需生成代理类。
  • 缺点:反射性能损耗;易出错(类型安全弱)。

2.2 手动实现 + Builder 模式

实现原理 :放弃接口声明,改用 Builder 链式调用
类似库:Volley、原生 OkHttp 封装

实现示例:
typescript 复制代码
public class ApiClient {
    private final String baseUrl;
    private final OkHttpClient client;
    
    public ApiClient(String baseUrl) {
        this.baseUrl = baseUrl;
        this.client = new OkHttpClient();
    }
    
    public RequestBuilder newRequest(String path) {
        return new RequestBuilder(baseUrl + path);
    }
    
    public static class RequestBuilder {
        private final String url;
        private String method = "GET";
        private Map<String, String> params = new HashMap<>();
        
        public RequestBuilder(String url) {
            this.url = url;
        }
        
        public RequestBuilder method(String method) {
            this.method = method;
            return this;
        }
        
        public RequestBuilder addParam(String key, String value) {
            params.put(key, value);
            return this;
        }
        
        public Call<List<Repo>> build() {
            HttpUrl.Builder urlBuilder = HttpUrl.parse(url).newBuilder();
            for (Map.Entry<String, String> entry : params.entrySet()) {
                urlBuilder.addQueryParameter(entry.getKey(), entry.getValue());
            }
            Request request = new Request.Builder()
                .url(urlBuilder.build())
                .method(method, null)
                .build();
            return new OkHttpCall<>(request);
        }
    }
}

// 使用方式
ApiClient client = new ApiClient("https://api.github.com/");
Call<List<Repo>> call = client.newRequest("users/octocat/repos")
    .addParam("sort", "created")
    .build();

2.3 、APT(注解处理器)方案

实现思路:

在编译时通过注解处理器生成接口实现类

less 复制代码
// 1. 定义注解
@Target(ElementType.METHOD)
@Retention(RetentionPolicy.SOURCE)
public @interface GET {
    String value();
}

// 2. 注解处理器生成实现类
public class ApiProcessor extends AbstractProcessor {
    @Override
    public boolean process(Set<? extends TypeElement> annotations, RoundEnvironment env) {
        for (Element element : env.getElementsAnnotatedWith(GET.class)) {
            // 使用 JavaPoet 生成类似下面的代码
            MethodSpec methodSpec = MethodSpec.methodBuilder("listRepos")
                .addParameter(String.class, "user")
                .returns(Call.class)
                .addStatement("String url = BASE_URL + \"users/\" + user + \"/repos\"")
                .addStatement("return new OkHttpCall<>(new Request.Builder().url(url).build())")
                .build();
            // 写入 Java 文件...
        }
        return true;
    }
}

// 3. 生成的实现类
public class GitHubServiceImpl implements GitHubService {
    @Override
    public Call<List<Repo>> listRepos(String user) {
        String url = "https://api.github.com/users/" + user + "/repos";
        return new OkHttpCall<>(new Request.Builder().url(url).build());
    }
}

// 4. 使用方式
GitHubService service = new GitHubServiceImpl();
Call<List<Repo>> call = service.listRepos("octocat");
问题: 那为什么Retrofit不用APT替代动态代理

理论是上可以的!
动态代理的价值 :Retrofit 的核心诉求是适配多变的应用场景(如调试模式切换、动态路由),运行时解析更符合这一目标

如下是更好的解释

2.4. APT 方案的挑战与局限

APT 更适用于生成固定逻辑的模板代码(如 ButterKnife)。

  • 灵活性受限

    动态代理可在运行时统一添加逻辑(如全局错误处理、日志拦截),而 APT 需为每个方法硬编码逻辑,复用性降低7。

  • 注解组合复杂度

    Retrofit 的注解(如 @Headers + @QueryMap)组合灵活,APT 需处理所有可能组合,生成代码复杂度高8。

  • 动态行为支持弱

    如动态 URL(@Url 注解)或运行时条件请求,APT 难以在编译时完全预生成逻辑3。

  • 要处理复杂注解 :像 Retrofit 的@Path@Query等注解的解析逻辑需要自己实现。

java 复制代码
// 注解处理器
@AutoService(Processor.class)
public class RetrofitProcessor extends AbstractProcessor {
    @Override
    public boolean process(Set<? extends TypeElement> annotations, RoundEnvironment roundEnv) {
        for (Element element : roundEnv.getElementsAnnotatedWith(Service.class)) {
            // 1. 解析接口
            TypeElement typeElement = (TypeElement) element;
            String className = typeElement.getSimpleName().toString() + "_Impl";
            
            // 2. 生成Java类
            JavaFileObject file = processingEnv.getFiler().createSourceFile(className);
            Writer writer = file.openWriter();
            
            // 3. 生成代码
            writer.write("public class " + className + " implements " + typeElement + " {\n");
            writer.write("  private final Retrofit retrofit;\n");
            writer.write("  public " + className + "(Retrofit retrofit) {\n");
            writer.write("    this.retrofit = retrofit;\n");
            writer.write("  }\n");
            
            // 4. 为每个方法生成实现
            for (Element method : typeElement.getEnclosedElements()) {
                if (method.getKind() == ElementKind.METHOD) {
                    ExecutableElement methodElement = (ExecutableElement) method;
                    // 解析方法注解并生成代码...
                }
            }
            
            writer.write("}");
            writer.close();
        }
        return true;
    }
}

// 生成的实现类示例
public class GitHubService_Impl implements GitHubService {
    private final Retrofit retrofit;
    
    public GitHubService_Impl(Retrofit retrofit) {
        this.retrofit = retrofit;
    }
    
    @Override
    public Call<List<Repo>> listRepos(String user) {
        // 自动生成的请求构建代码...
    }
}

3. Retrofit的适配器模式解读?

无论使用 RxJava 还是协程适配器,Retrofit 适配器模式始终解决三个核心问题

总结:适配器选择建议

  1. Kotlin 项目 → 首选 协程 suspend 函数(Retrofit 2.6.0+ 原生支持)
  2. 响应式项目RxJava (Android) / Reactor (Spring)
  3. Java 项目CompletableFuture (Java 8+) 或 RxJava
  4. 特殊需求 → 自定义适配器(如 OptionalResult 封装)

4. Retrofit的设计模式有哪些?

4.1 Retrofit 设计模式总结表

设计模式 应用场景 关键实现点 优势
动态代理模式 声明式 API 接口实现 Proxy.newProxyInstance() 生成接口代理 免去手动实现接口逻辑
建造者模式 构建 Retrofit 复杂对象 Retrofit.Builder() 链式配置参数 隔离构建逻辑,支持灵活配置
适配器模式 兼容不同异步处理机制 CallAdapter 转换 Call 为 RxJava/协程等类型 解耦 HTTP 请求与异步处理方式
工厂模式 动态创建转换器/适配器 Converter.Factory 创建 JSON/XML 解析器 扩展性强,支持自定义数据格式
外观模式 简化网络库调用复杂度 Retrofit 类封装 OkHttp/解析/线程切换等底层逻辑 提供统一简洁的 API 入口
策略模式 动态切换数据解析策略 可配置多种 Converter.Factory (Gson/Moshi) 灵活替换算法,满足不同需求
装饰器模式 增强网络请求功能 OkHttp 拦截器链添加日志/缓存/重试逻辑 动态扩展功能,符合开闭原则

4.2 模式协作说明

  1. 入口 :用户通过动态代理定义 API 接口

  2. 配置建造者模式 组装 Retrofit 实例

  3. 扩展

    • 工厂模式 生成数据转换器 (Converter)
    • 适配器模式兼容异步框架 (RxJava/协程)
  4. 执行

    • 外观模式统一调用网络层 (OkHttp)
    • 策略模式动态切换数据解析方式
  5. 增强装饰器模式通过拦截器添加额外功能

项目的地址: github.com/pengcaihua1...

相关推荐
然我14 分钟前
react-router-dom 完全指南:从零实现动态路由与嵌套布局
前端·react.js·面试
一_个前端22 分钟前
Vite项目中SVG同步转换成Image对象
前端
202623 分钟前
12. npm version方法总结
前端·javascript·vue.js
用户876128290737424 分钟前
mapboxgl中对popup弹窗添加事件
前端·vue.js
帅夫帅夫25 分钟前
JavaScript继承探秘:从原型链到ES6 Class
前端·javascript
a别念m25 分钟前
HTML5 离线存储
前端·html·html5
goldenocean1 小时前
React之旅-06 Ref
前端·react.js·前端框架
子林super1 小时前
【非标】es屏蔽中心扩容协调节点
前端
前端拿破轮1 小时前
刷了这么久LeetCode了,挑战一道hard。。。
前端·javascript·面试
代码小学僧1 小时前
「双端 + 响应式」企业官网开发经验分享
前端·css·响应式设计