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...

相关推荐
遗憾随她而去.1 天前
前端浏览器缓存深度解析:从原理到实战
前端
万行1 天前
企业级前后端认证方式
前端·windows
2501_948120151 天前
基于Vue 3的可视化大屏系统设计
前端·javascript·vue.js
Jinuss1 天前
源码分析之React中createFiberRoot方法创建Fiber根节点
前端·javascript·react.js
Jinuss1 天前
源码分析之React中ReactDOMRoot实现
前端·javascript·react.js
web守墓人1 天前
【前端】vue3的指令
前端
想起你的日子1 天前
EFCore之Code First
前端·.netcore
框架图1 天前
Html语法
前端·html
深耕AI1 天前
【wordpress系列教程】07 网站迁移与备份
运维·服务器·前端·数据库