学习笔记
📚AI大数据相关
00. Ai大数据模型
模型整理
使用 DeepSeek 通用公式
学会这8招,让DeepSeek变得超好用!
大数据购物分析选优
Windows和Ubuntu部署DeepSeek性能差距
本地部署 Ollam+DeepSeek 探索爬坑
模型对比测试
React Native 和 native 半屏弹窗
React Native Modal + WebView
01. Ai 提示词
00. 🌟提示词黄金公式:让 AI 秒变专家的通用指令框架
01. 🚀 AI短篇小说创作:大神级实操指南与提示词模板
02. Gemini专用Agent:今日头条硬核技术博主
03. Gemini专用Agent:小红书金牌育儿养生博主
04. Gemini专业Agent:全栈架构导师 & 终极面试官
05. Gemini专业Agent:资深私募操盘手 & 首席证券分析师
06. 🤖IDE&&CLI使用Agent:单元测试
07. 高度自主化开发提示词
Java基础相关
JVM内存模型及线程空间
动态代理
java并发编程
Java基础知识
Java中Future
Java中9种常见的CMS GC问题分析与解决
移动端相关
杂乱整理
HarmonyOS 鸿蒙开发知识
ArkTS中如何自定义组件和复用统一样式
Android开发相关
WebView设置圆角
📄 Android 实时屏幕共享技术方案文档
Android线程与线程池全面解析:从使用到源码剖析
🔥 RecyclerView全面解析:从架构原理到性能优化与面试指南
🔥 OkHttp全面解析:从架构原理到性能优化与面试指南
🔥 Retrofit全面解析:从设计架构与核心思想到常见问题
开发工具相关
Git cmd学习整理
Markdown用法大全集
【2023年12月】工作常用
Git如何单独合并某次提交到另一个分支
Debina/Ubuntu安装Node环境
🐳 Docker 命令行与 Docker Compose 全面指南
使用Podman来替代Docker
中文注释提交git后变成"\u0087\u0079..."问题
macOS 容器化工具深度解析:Docker Desktop、OrbStack、Podman 及其他替代方案对比
前端开发相关
Node+TypeScript相关记录
TypeScript 读写 MariaDB
Node TypeScript项目 token生成、管理及拦截校验的实现
TypeScript+Express创建和实现一个服务示例
Express接口处理器抽取注册方式
Express 实现 RESTful API
创建 TypeScript Express 项目,并配置直接用 npm start 运行
TypeScript + Express 实现文件下载接口
export 和 export default的区别
TypeScript+Express 实现用户注册和登录接口
TypeScript 和 JavaScript 中,`===` 和 `==`
CSS中的尺寸大小标准
小程序px和rpx
使用Python快速处理Excel的合并拆分
读书写作相关
一些句子01
李敖语录
罗翔老师的一些经典语句
周易相关知识
周易是对自然描述还是为自然立法
40句落寞诗词,穿透柔魂弱魄
杂玩整理
黑苹果睿频问题
基于纯Linux自己部署Nas构思
Ubuntu换源
Ubuntu挂载tf卡
Ubuntu运行Docker报错
Ubuntu安装运行Docker报错处理
官方镜像安装Docker
Docker 设置root dir 切换数据到其他存储位置
systemctl stop docker 报错
NextCloud安装ffmpeg 显示视频缩略图
Docker源不生效解决方式
Docker源不生效解决方式II——搭建docker-hub镜像
搞定群晖总Docker部署gitea启用ssh协议
MacOS一键安装命令软件列表
群晖ssl证书目录
Android通过ADB命令播放视频
[完成] 群晖自动更新https证书项目
Linux设备整机限速
Linux限速2
Gitea部署Runner服务
精选网站
有声主播知识
学习笔记
有声主播入门到进阶
有声主播新手的入门练手内容推荐
DeepSeek分析喜马拉雅旗下 喜播平台 的有声主播培训
录书设备资料1
典故专辑资料整理
0B. 脚本和大纲
0A. 前置准备资料
临时
Linux下对设备进行限速
C++ 学习
01 Android开发学习C++
其他资料分组
【面试相关】💡 面试后、Offer前,可以主动了解和确认的信息
【工作经验】会计师事务所工作中如何有效管理和规避法律风险
职业发展规划
-
+
home page
🔥 Retrofit全面解析:从设计架构与核心思想到常见问题
### 设计模式与架构理念 Retrofit 之所以在 Android 网络库中备受青睐,很大程度上得益于其精巧的设计模式运用和对 RESTful API 的深度支持。 **1. 门面模式简化接口**:Retrofit 通过定义接口并使用注解来配置网络请求参数,为开发者提供了简洁统一的 API。这种设计让使用者无需关心 OkHttp 的复杂细节,只需关注业务逻辑。例如,一个简单的请求接口定义如下: ```java public interface ApiService { @GET("user/{id}") Call<User> getUser(@Path("id") String userId); } ``` **2. 动态代理实现透明调用**:Retrofit 使用动态代理技术,在运行时生成接口的实现。当你调用 `Retrofit.create(ApiService.class)` 时,Retrofit 会返回一个代理对象,拦截方法调用并解析注解,将其转换为网络请求。 ```java // Retrofit 的 create 方法核心逻辑 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.callAdapter.adapt(okHttpCall); } }); } ``` **3. RESTful API 规范支持**:Retrofit 强制遵循 HTTP 协议规范,例如在 GET 请求中添加 Body 会直接报错,这种设计保证了代码的规范性和可维护性。 ### 四个核心接口 Retrofit 的设计围绕四个核心接口展开,这也是理解其架构的关键: | 接口 | 职责 | 说明 | | :-------------- | :------------------- | :------------------------------ | | **Call** | 表征一个 HTTP 请求 | 提供执行请求的方法(同步/异步) | | **CallAdapter** | 适配 Call 到其他类型 | 支持 RxJava、Guava 等 | | **Converter** | 数据序列化/反序列化 | 支持 Gson、Jackson 等 | | **Callback** | 处理异步响应 | 成功/失败的回调 | ## 🔍 Retrofit 源码与核心原理 ### 创建过程:建造者模式 Retrofit 使用建造者模式进行配置,核心代码如下: ```java Retrofit retrofit = new Retrofit.Builder() .baseUrl("https://api.example.com/") .client(okHttpClient) // 自定义 OkHttpClient .addConverterFactory(GsonConverterFactory.create()) // 数据转换器 .addCallAdapterFactory(RxJava2CallAdapterFactory.create()) // 调用适配器 .build(); ``` 在 `build()` 方法中,Retrofit 会初始化几个关键组件: - **callFactory**:通常是 OkHttpClient,负责实际网络请求 - **converterFactories**:数据转换器工厂列表,用于处理请求和响应数据 - **adapterFactories**:调用适配器工厂列表,用于适配不同的执行环境 - **callbackExecutor**:回调执行器,用于将结果切换到主线程 ### 请求执行流程 **1. 注解解析与 ServiceMethod 构建** 当第一次调用接口方法时,Retrofit 会解析方法注解,构建 ServiceMethod 对象并缓存: ```java // Retrofit 中 loadServiceMethod 方法的核心逻辑 ServiceMethod loadServiceMethod(Method method) { ServiceMethod result = serviceMethodCache.get(method); if (result != null) return result; synchronized (serviceMethodCache) { result = ServiceMethod.parseAnnotations(this, method); serviceMethodCache.put(method, result); return result; } } ``` **2. OkHttpCall 创建与适配** ServiceMethod 解析后会创建 OkHttpCall,然后通过 CallAdapter 进行适配: ```java // Retrofit create 方法中的关键调用 ServiceMethod serviceMethod = loadServiceMethod(method); OkHttpCall okHttpCall = new OkHttpCall(serviceMethod, args); return serviceMethod.callAdapter.adapt(okHttpCall); ``` **3. 网络请求执行** 最终,OkHttpCall 会使用配置的 OkHttpClient 执行实际网络请求,并通过 Converter 处理响应数据。 ### Kotlin 协程支持 Retrofit 通过检测方法的最后一个参数是否为 `Continuation` 类型来判断是否为挂起函数: ```kotlin // Retrofit 对协程方法的判断逻辑 private ParameterHandler<?> parseParameter(...) { // ... 解析其他参数注解 if (result == null && allowContinuation) { if (Utils.getRawType(parameterType) == Continuation.class) { isKotlinSuspendFunction = true; return null; } } } ``` 编译时,Kotlin 编译器会自动为挂起函数添加一个 `Continuation` 参数,Retrofit 利用这一特性实现协程支持。 ## 🛠️ Retrofit API 使用与常见问题 ### 基础使用 **1. 接口定义** ```java public interface ApiService { @GET("user/{id}") Call<User> getUser(@Path("id") String userId); @POST("user/create") Call<ApiResponse> createUser(@Body User user); @GET("search") Call<List<Item>> search(@Query("q") String query); } ``` **2. Retrofit 实例化** ```java OkHttpClient okHttpClient = new OkHttpClient.Builder() .connectTimeout(10, TimeUnit.SECONDS) .readTimeout(30, TimeUnit.SECONDS) .build(); Retrofit retrofit = new Retrofit.Builder() .baseUrl("https://api.example.com/") .client(okHttpClient) .addConverterFactory(GsonConverterFactory.create()) .build(); ApiService apiService = retrofit.create(ApiService.class); ``` **3. 发起请求** ```java // 同步请求 try { Response<User> response = apiService.getUser("123").execute(); if (response.isSuccessful()) { User user = response.body(); } } catch (IOException e) { e.printStackTrace(); } // 异步请求 apiService.getUser("123").enqueue(new Callback<User>() { @Override public void onResponse(Call<User> call, Response<User> response) { if (response.isSuccessful()) { User user = response.body(); // 更新 UI } } @Override public void onFailure(Call<User> call, Throwable t) { // 处理失败 } }); ``` ### 常见问题与解决方案 **1. 数据格式不匹配导致解析错误** > 问题:服务器返回 JSON 对象,但客户端期望 JSON 数组,导致 "JSONObject$1 cannot be cast to JSONArray" 错误。 **解决方案**: - 确保数据模型类与 API 响应格式匹配 - 使用统一的响应包装器: ```java // 统一的 API 响应包装类 public class ApiResponse<T> { public int code; public String message; public T data; } // 接口定义使用包装类型 public interface ApiService { @GET("user/{id}") Call<ApiResponse<User>> getUser(@Path("id") String userId); } ``` **2. 主线程网络请求** > 问题:在主线程执行同步网络请求导致 ANR。 **解决方案**: - 始终在后台线程执行同步请求 - 使用 Retrofit 的异步 `enqueue` 方法 - 结合 RxJava 时正确配置线程调度: ```java apiService.getUser("123") .subscribeOn(Schedulers.io()) .observeOn(AndroidSchedulers.mainThread()) .subscribe(user -> { // 更新 UI }, throwable -> { // 处理错误 }); ``` **3. 空指针异常** > 问题:响应体为 null 时直接使用导致 NPE。 **解决方案**: - 始终检查响应体和数据有效性: ```java apiService.getUser("123").enqueue(new Callback<User>() { @Override public void onResponse(Call<User> call, Response<User> response) { if (response.isSuccessful() && response.body() != null) { User user = response.body(); // 安全使用 user } else { // 处理空响应 ResponseBody errorBody = response.errorBody(); // 解析错误信息 } } }); ``` **4. RecyclerView 适配器数据更新崩溃** > 问题:在数据为空或格式不正确时更新适配器导致崩溃。 **解决方案**: - 确保数据有效性,在 UI 线程更新适配器: ```java apiService.getData().enqueue(new Callback<List<DataModel>>() { @Override public void onResponse(Call<List<DataModel>> call, Response<List<DataModel>> response) { if (response.isSuccessful() && response.body() != null) { List<DataModel> data = response.body(); runOnUiThread(() -> { adapter.setData(data); adapter.notifyDataSetChanged(); }); } } }); ``` ## ⚡ Retrofit 性能优化与高级特性 ### 1. 合理配置 OkHttpClient ```java // 创建优化后的 OkHttpClient OkHttpClient okHttpClient = new OkHttpClient.Builder() .connectTimeout(10, TimeUnit.SECONDS) .readTimeout(30, TimeUnit.SECONDS) .writeTimeout(30, TimeUnit.SECONDS) .connectionPool(new ConnectionPool(5, 5, TimeUnit.MINUTES)) // 连接池 .cache(new Cache(context.getCacheDir(), 10 * 1024 * 1024)) // 10MB 缓存 .addInterceptor(new LoggingInterceptor()) // 日志拦截器 .addNetworkInterceptor(new StethoInterceptor()) // 调试拦截器 .build(); ``` ### 2. 自定义 CallAdapter 实现自定义 CallAdapter 可以统一处理 API 错误和异常: ```java // 自定义 CallAdapter 工厂 public class ApiResponseCallAdapterFactory extends CallAdapter.Factory { @Override public CallAdapter<?, ?> get(Type returnType, Annotation[] annotations, Retrofit retrofit) { // 返回自定义 CallAdapter return new ApiResponseCallAdapter(returnType); } static class ApiResponseCallAdapter<R> implements CallAdapter<R, ApiResponse<R>> { private final Type responseType; ApiResponseCallAdapter(Type responseType) { this.responseType = responseType; } @Override public Type responseType() { return responseType; } @Override public ApiResponse<R> adapt(Call<R> call) { return new ApiResponse<>(call); } } } ``` ### 3. 自定义 Converter 处理非标准响应格式: ```java public class XmlConverterFactory extends Converter.Factory { public static XmlConverterFactory create() { return new XmlConverterFactory(); } @Override public Converter<ResponseBody, ?> responseBodyConverter(Type type, Annotation[] annotations, Retrofit retrofit) { // 返回 XML 转换器 return new XmlResponseBodyConverter<>(type); } } ``` ### 4. 请求缓存策略 通过拦截器实现智能缓存: ```java // 缓存拦截器 public class CacheInterceptor implements Interceptor { @Override public Response intercept(Chain chain) throws IOException { Request request = chain.request(); // 只在有网络时检查缓存 if (isNetworkAvailable()) { request = request.newBuilder() .header("Cache-Control", "public, max-age=" + 60) .build(); } else { request = request.newBuilder() .header("Cache-Control", "public, only-if-cached, max-stale=" + 7 * 24 * 60 * 60) .build(); } return chain.proceed(request); } } ``` ## 💯 Retrofit 面试高频问题 ### 1. Retrofit 与 OkHttp 的关系是什么? **参考答案**:Retrofit 是基于 OkHttp 的 RESTful HTTP 客户端,负责网络请求接口的封装,而实际的网络请求工作由 OkHttp 完成。Retrofit 通过注解配置请求参数,利用动态代理生成接口实现,然后委托 OkHttp 执行请求。 ### 2. Retrofit 中的动态代理起到了什么作用? **参考答案**:动态代理是 Retrofit 的核心机制,它在运行时创建接口的代理对象。当调用接口方法时,InvocationHandler 会拦截调用,解析方法注解和参数,构建 ServiceMethod,并创建 OkHttpCall。这种设计实现了请求配置与执行的解耦。 ### 3. CallAdapter 和 Converter 的区别是什么? **参考答案**: - **CallAdapter**:适配 Call 到其他类型,如 RxJava 的 Observable、CompletableFuture 等,主要处理调用方式的适配。 - **Converter**:负责请求体和响应体的序列化与反序列化,如 JSON 到 Java 对象的转换,主要处理数据格式的转换。 ### 4. Retrofit 如何支持 Kotlin 协程? **参考答案**:Retrofit 通过检测挂起函数的最后一个参数是否为 Continuation 类型来判断协程调用。在编译时,Kotlin 编译器会自动为挂起函数添加 Continuation 参数,Retrofit 利用这一特性,返回的 Call 对象会自动适配协程。 ### 5. Retrofit 的注解解析过程是怎样的? **参考答案**:注解解析主要在 ServiceMethod.parseAnnotations() 中完成: - 解析方法注解(GET/POST等)确定请求方法和路径 - 解析参数注解(Path/Query/Body等)构建参数处理器 - 使用 Converter 和 CallAdapter 工厂创建相应的实例 ### 6. 如何优化 Retrofit 的网络请求? **参考答案**: - 合理配置 OkHttpClient 的连接超时和缓存 - 使用连接池复用 HTTP 连接 - 添加合适的拦截器处理日志、缓存和重试 - 使用 Protobuf 等更高效的数据格式替代 JSON ### 7. Retrofit 中如何统一处理错误响应? **参考答案**: - 自定义 Converter 统一解析错误响应体 - 使用拦截器检查 HTTP 状态码和业务状态码 - 实现自定义 CallAdapter 统一封装 API 响应 - 通过 BaseCallback 或 BaseObserver 集中处理错误 ### 8. 为什么 Retrofit 接口不能声明为 final 或 private? **参考答案**:因为 Retrofit 使用动态代理生成接口实现,动态代理要求接口和方法必须是可继承和覆盖的。final 或 private 方法无法被代理对象覆盖,因此会导致运行时错误。 ### 结语 Retrofit 的成功在于其精巧的架构设计和对开发体验的深度优化。通过面向接口的编程模型、灵活的可扩展性接口和简洁的 API 设计,它成为了 Android 网络库的首选。深入理解其原理,不仅能帮助解决实际开发中的问题,还能提高代码设计和架构能力。
嘿手大叔
Nov. 14, 2025, 4:32 p.m.
转发文档
Collection documents
Last
Next
手机扫码
Copy link
手机扫一扫转发分享
Copy link
Markdown文件
share
link
type
password
Update password