第二章:Retrofit 网络请求流程分析——从创建 Call 到响应的完整链路,DNS 解析在哪
咱们做 Android 开发的,天天都在用 Retrofit 发请求。但你敢拍胸脯说,你清楚一个请求从创建 Call 到最后拿到响应,中间走了多少步吗?我个人习惯,每次遇到网络性能问题,第一件事就是把这整条链路在脑子里过一遍。说白了,你不摸清每个环节的耗时,优化就无从下手。
2.1 整体链路:从一行代码到一个 HTTP 响应
先看一个最普通的调用:
// Retrofit 接口定义
interface ApiService {
@GET("users/list")
suspend fun getUsers(): List<User>
}
// 使用时
val call = apiService.getUsers() // 创建 Call
val response = call.awaitResponse() // 执行请求
你看到的就这两行。但背后,OkHttp 帮你干了大概这么几件事:
- Retrofit 将你的 Kotlin 函数调用转为
OkHttpClient.Call对象 - Call 被放入 拦截器链(Interceptor Chain)
- 拦截器链依次执行:重试 → 桥接 → 缓存 → 连接(DNS + TCP + TLS) → 发送请求 → 读取响应
- 最终 Response 回到你的协程或回调里
嗯,这里面最关键、也是最耗时的,就是第 3 步里的 连接阶段。而 DNS 解析,正是连接阶段的第一步。
2.2 创建 Call —— 这一步几乎不耗时
Retrofit 通过动态代理,把 apiService.getUsers() 转换成 OkHttpClient.Call。这里我提一句,我见过有人觉得这里慢,其实大可不用担心。它只是创建了一个对象,没有网络 IO,连线程切换都没有。真正花钱的地方在后面。
2.3 执行 Call —— 拦截器链正式启动
不管你是用 enqueue() 还是 execute(),最终都会走到 RealCall.getResponseWithInterceptorChain()。这方法里裹着一个 ArrayList,按顺序塞了 5 个内置拦截器 + 你自定义的拦截器。然后一个个调过去。
我习惯把这链条想象成流水线。每一个拦截器都有机会“动手脚”:比如重试、添加通用头、读缓存等等。但这里面的老大难,始终是 ConnectInterceptor。
2.4 DNS 解析 —— 真实发生的位置
你跟着代码走一圈就会看到,ConnectInterceptor 里调用了 exchangeFinder.find(),接着走到 RouteSelector 的 next() 方法。在这里,OkHttp 会调用你自己设置的 Dns 实现。默认就是 Dns.SYSTEM,走系统 API——说白了就是 InetAddress.getAllByName()。
2.5 连接耗时:TCP + TLS 握手
DNS 解析拿到 IP 后,OkHttp 会尝试验证已有连接池里有没有可复用的连接。没有?那就新建 TCP 连接。如果用了 HTTPS,还得再加一次 TLS 握手。这两步加起来,往少了说几十毫秒,多的能有几秒。
| 阶段 | 典型耗时 | 备注 |
|---|---|---|
| DNS 解析 | 10~200 ms | 取决于 DNS 服务器和网络 |
| TCP 连接 | 20~100 ms | 一个 RTT |
| TLS 握手 | 100~500 ms | 根据加密套件,通常 1~2 个 RTT |
| 请求传输 + 响应 | 依数据量而定 | 这个优化不在本章讨论 |
2.6 一个小实验:验证 DNS 耗时
你可以在 Retrofit 初始化时,给 OkHttpClient 加一个自定义 Dns:
val client = OkHttpClient.Builder()
.dns(object : Dns {
override fun lookup(hostname: String): List<InetAddress> {
val start = System.nanoTime()
val result = Dns.SYSTEM.lookup(hostname)
val elapsed = (System.nanoTime() - start) / 1_000_000
Log.d("DNS", "$hostname 解析耗时 $elapsed ms")
return result
}
})
.build()
跑一下你的 App,看看那些接口的 DNS 用了多长时间。你会吓一跳的——有些 CDN 域名解析能到 500+ ms。这就是优化的起点。
2.7 本章小结
Retrofit 的请求链路,本质就是 OkHttp 的拦截器链。DNS 解析藏在 ConnectInterceptor 里,是最容易被忽略的耗时元凶。下次你再觉得某个接口慢,别急着优化 JSON 解析或者压缩图片——先看看 连接阶段 花了多久。连接慢,后面再快也无济于事。