网络请求与 Retrofit 面试知识点整理
整理自模拟面试,覆盖 OkHttp、HTTPS、拦截器、Retrofit、动态代理、ServiceMethod 缓存,以及结合 PicPat widget 的责任链类比
一、OkHttp 核心原理
一句话
- OkHttp 本质上是一个高性能 HTTP 客户端,负责把请求发送、连接复用、协议协商、缓存、重试、拦截器链这些底层能力组织起来。
核心组件
- OkHttpClient:全局配置入口,承载超时、连接池、拦截器、DNS、代理、缓存等能力
- Request / Response:请求与响应的数据抽象
- Call:一次 HTTP 调用,可同步
execute() 或异步 enqueue()
- Dispatcher:异步任务调度器,控制全局并发和单 Host 并发
- Interceptor:责任链式扩展点,负责把请求处理拆成多个阶段
- ConnectionPool:连接池,复用 TCP/TLS 连接,降低握手和建连成本
一次请求的主流程
- 业务层构建
Request
OkHttpClient.newCall(request) 生成 Call
- 异步场景由
Dispatcher 调度执行
- 请求进入拦截器链
- 尝试从连接池获取可复用连接,否则执行 DNS、TCP、TLS 握手
- 写出请求数据并读取响应
- 解析为
Response 返回给调用方
为什么 OkHttp 性能好
- 连接复用:避免频繁创建和销毁 TCP/TLS 连接
- HTTP/2 支持:支持多路复用,减少队头阻塞
- 任务调度:通过
Dispatcher 控制并发
- 缓存能力:遵循 HTTP 语义做本地缓存
- 责任链扩展:公共逻辑统一收敛在拦截器,不侵入业务代码
二、OkHttp 拦截器机制
本质
- 拦截器本质是 责任链模式
- 请求从前往后经过多个拦截器
- 响应按相反方向返回
- 每个拦截器都可以在
chain.proceed(request) 前后做增强处理
典型执行顺序
Interceptor A before
Interceptor B before
Interceptor C before
发起网络请求
Interceptor C after
Interceptor B after
Interceptor A after
内置拦截器的职责
- RetryAndFollowUpInterceptor
负责失败重试、重定向、部分鉴权跟进
- BridgeInterceptor
补齐协议层请求头,处理 gzip、cookie 等桥接逻辑
- CacheInterceptor
根据 HTTP 缓存语义决定走缓存、网络还是条件请求
- ConnectInterceptor
建立连接或从连接池中复用连接
- CallServerInterceptor
真正执行网络 I/O,把请求写到服务端并读取响应
Application Interceptor 和 Network Interceptor 的区别
- Application Interceptor
- 通过
addInterceptor() 添加
- 更偏业务语义
- 适合统一加 token、公共参数、日志、业务错误处理
- Network Interceptor
- 通过
addNetworkInterceptor() 添加
- 更接近真实网络传输
- 适合观察真实网络 header、压缩、重定向、多次网络请求过程
常见注意点
- 大多数拦截器应且只应调用一次
proceed()
- 可以改写请求,也可以包装响应
ResponseBody 是流式的,消费后通常不能重复读取
- 日志拦截如果直接把 body 读完,可能导致下游读不到内容
三、HTTPS / TLS 加密
一句话
- HTTPS = HTTP + TLS,TLS 负责加密传输、完整性校验和服务端身份认证。
HTTPS 主要解决的问题
- 机密性:传输内容不会被旁路明文看到
- 完整性:数据被篡改时能够被发现
- 身份认证:客户端能确认访问的是目标服务端,而不是中间人
为什么是混合加密
- 非对称加密:用于身份认证和密钥协商
- 对称加密:用于实际业务数据传输
原因:
- 非对称加密安全但开销大
- 对称加密速度快,适合传输大量数据
TLS 握手流程
- Client Hello
客户端发出支持的 TLS 版本、加密套件、随机数、扩展能力
- Server Hello
服务端返回选中的 TLS 版本、加密套件、随机数和证书链
- 客户端校验证书
校验证书链、域名、有效期、签名等
- 密钥协商
现代 TLS 常用 ECDHE 等方式协商会话密钥
- 开始对称加密通信
后续 HTTP 数据主要用协商出的会话密钥传输
OkHttp 里的 HTTPS 关注点
- TLS 握手建立在 TCP 连接之后
- 默认依赖平台
SSLSocket、TrustManager、HostnameVerifier
- 常见增强手段是 certificate pinning
- 连接池会复用 TLS 连接,降低重复握手成本
面试易错点
- HTTPS 保护的是 传输链路
- 不代表业务逻辑、本地存储、日志输出天然安全
- 如果错误放开证书校验或 Hostname 校验,HTTPS 价值会被显著削弱
四、Retrofit 是如何包装 OkHttp 的
一句话
- Retrofit 本质上不是重新实现网络库,而是在 OkHttp 之上增加了一层 声明式接口调用能力。
职责划分
- OkHttp 负责
- 请求发送
- 连接复用
- TLS
- 缓存
- 拦截器
- HTTP/2
- Retrofit 负责
- 动态代理接口
- 解析方法注解和参数注解
- 构建
Request
- 适配返回值调用模型
- 响应体序列化与反序列化
从接口调用到网络请求的流程
Retrofit.create(ApiService::class.java) 生成动态代理对象
- 调用接口方法时,代理对象拦截方法调用
- Retrofit 解析注解、参数和返回值类型
- 把方法信息组织成请求模型
- 构建底层 OkHttp
Request
- 创建并委托底层 OkHttp
Call
- 通过
CallAdapter 适配成 Call<T>、协程、RxJava 等形式
- 通过
Converter 把 ResponseBody 转成业务对象
Converter 和 CallAdapter 的职责
- Converter
- 请求对象 ->
RequestBody
ResponseBody -> 业务对象
- CallAdapter
- 把底层
Call 适配成目标调用模型
- 如
Call<T>、suspend、Observable<T>、Single<T>
五、Retrofit 动态代理原理
本质
- Retrofit 通过 JDK 动态代理,为接口在运行时生成代理对象
- 接口方法不会直接执行
- 所有方法调用最终都会进入统一的
InvocationHandler.invoke()
工作流程
create() 时用 Proxy.newProxyInstance() 创建接口代理对象
- 调用接口方法时拿到:
- 当前
Method
- 方法注解
- 参数值和参数注解
- 返回值类型
- Retrofit 把这个
Method 解析成内部的 ServiceMethod
- 再把本次调用参数套入模板,生成请求
- 创建底层 OkHttp
Call
- 用
Converter 和 CallAdapter 完成结果转换与调用适配
为什么动态代理不是 Retrofit 的核心本质
- 动态代理解决的是:如何拦截接口方法调用
- 注解解析解决的是:如何把接口方法翻译成 HTTP 请求
结论:
- 注解解析才是 Retrofit 的核心
- 动态代理更像是调用入口和承载方式
六、ServiceMethod 缓存
是什么
ServiceMethod 可以理解为 Retrofit 对某个接口方法解析后的“执行模板”
- 里面会缓存:
- HTTP method
- 路径模板
- 参数处理规则
CallAdapter
Converter
- 返回值适配信息
生命周期
ServiceMethod 不是全局静态缓存
- 它通常挂在某个
Retrofit 实例内部
- 只要这个 Retrofit 实例还存活,对应缓存就还在
- 当
Retrofit 实例被释放,对应缓存也随之释放
加载时机
- 默认是 懒加载
- 第一次调用某个接口方法时才解析并缓存
- 后续再次调用相同方法时直接复用
会不会有性能问题
- Retrofit 确实依赖反射
- 但反射主要发生在接口方法首次解析阶段
- 后续走
ServiceMethod 缓存,成本很低
- 相比网络 I/O、JSON 解析、TLS 握手,这部分通常不是主瓶颈
是否支持初始化时全部预加载
- 默认不全量预加载
- 标准行为是按需解析
- 如果业务有特殊需求,可以自行做预热,但通常没有必要
七、为什么有时直接用 OkHttp,不一定用 Retrofit
更适合直接用 OkHttp 的场景
- 流式响应,如 SSE、分块读取、长连接流式输出
- 文件上传下载
- WebSocket 或更底层协议控制
- 特殊请求链路,对 header / body / 读取时机控制特别细
- 接口数量不多,但协议控制要求高
更适合 Retrofit 的场景
- 常规 RESTful API
- 接口数量多,希望统一接口声明
- 强依赖协程 / RxJava / JSON 转换器
- 希望降低样板代码、提升团队开发效率
八、结合 PicPat 项目理解责任链
类比点
- OkHttp 用 Interceptor 把网络请求拆成责任链
- PicPat 的 widget 渲染把
RemoteViews 构建拆成了分层装饰链
对应关系
Layer 接口里的 decorate(context, chain) 类似 Interceptor 的 intercept(chain)
chain.proceed(widgetInfo) 类似 chain.proceed(request)
RealLayerChain 负责串起整个处理流程
典型层级
BasisLayer:基础布局
BackgroundLayer:背景
PhotoLayer:主图
FrameLayer:边框
PendantLayer:挂件
AvatarLayer:头像与昵称
VideoLayer:视频态
EventLayer:点击事件
为什么这里用责任链,不直接用 Builder
- Builder 更适合“收集参数后一次性 build 对象”
- widget 更像“基于基础布局逐层装饰”
- 每一层都可能按条件跳过、插拔、重排
- 更适合后续做主题扩展和 partial update
partial update 怎么和 Layer 配合
- 全量更新:走完整 Layer 链,生成完整
RemoteViews
- 局部更新:只复用某一层,或复用裁剪后的子链
- 最后用
partiallyUpdateAppWidget() 做局部刷新
典型例子:
- 头像变化:只复用 AvatarLayer
- 皮肤变化:可走完整链,也可走裁剪后的皮肤相关子链
- 照片变化:如果联动 shape、frame、pendant,很多时候直接走更稳妥的全量更新
九、一句话速记
| 知识点 |
速记 |
| OkHttp 本质 |
高性能 HTTP 客户端,负责连接、调度、缓存、拦截器 |
| Interceptor |
责任链模式,请求正向走,响应反向回 |
| HTTPS |
HTTP + TLS,解决机密性、完整性、身份认证 |
| Retrofit |
在 OkHttp 之上做声明式接口包装 |
| 动态代理 |
负责接住接口调用,不是 Retrofit 的核心本质 |
| 注解解析 |
把接口方法翻译成请求模型,才是 Retrofit 核心 |
| ServiceMethod |
接口方法解析结果模板,挂在 Retrofit 实例内缓存 |
| 反射性能 |
有一次性开销,但通常不是网络场景主瓶颈 |
| PicPat 类比 |
widget 分层装饰链,本质上是 UI 版 Interceptor 思路 |