OkHttp ResponseBody 三种读取方法完整对比
·
一、底层统一基础
三者底层共用同一份 TCP 网络流、同一套内核 TCP 缓冲区,流只能读取一次,任意一个方法调用后,另外两个无法再使用。 内部源头都是 Okio BufferedSource,只是对外封装、读取逻辑、生命周期控制不同。
二、逐个拆解原理
1. string()
底层源码逻辑
- 内部调用
source()获取缓冲流; - 循环读取直到
exhausted() = true,把全部响应字节加载到内存; - 字节数组转 UTF-8 字符串;
- 自动关闭流、释放 TCP 连接。
核心特性
- 读取模式:一次性全量加载
- 内存:响应多大,堆内存占用多大,大响应极易 OOM
- 控制权:完全交给 OkHttp,开发者无法干预分段读取
- 连接:读完立刻关闭,不支持长连接 / 流式推送
适用场景
小接口、短 JSON、一次性返回结果;禁止用于 SSE、大文件、长流
2. source()
底层源码逻辑
- 直接返回包装好的
BufferedSource(Okio 带缓冲输入流); - 不会主动读取任何数据,懒加载;
- 内置应用层 Buffer,自动批量从内核缓冲区拉取数据,减少用户态 / 内核态切换;
- 读取、关闭连接完全由开发者手动控制。
核心特性
- 读取模式:按需分段读取,读一段丢一段,内存稳定
- 丰富 API:
readUtf8Line()、readByteString()、exhausted()等流式专用方法 - 连接:不自动关闭,可维持 TCP 长连接持续接收推送数据
- 缓冲:自带高性能分段缓冲,无需手动包装
适用场景
SSE 流式输出、日志长推送、大文件分段下载、需要持续监听数据
3. byteStream()
底层源码逻辑
- 内部先获取
source(); - 通过适配器转换为 JDK 原生
InputStream对外暴露; - 原生 InputStream无内置缓冲,单次单字节 read 会频繁触发系统调用。
核心特性
- 读取模式:原生字节流,无分行、字符串快捷方法
- 缓冲:原生无缓冲,想要缓冲必须手动套
BufferedInputStream - 兼容性:标准 Java IO 流,适配老旧第三方工具、文件工具类
- 连接:需手动调用 close 关闭流释放连接
适用场景
兼容只接收 InputStream 的老旧 SDK、文件导出、第三方 IO 工具适配
三、核心区别对照表
| 维度 | string() |
source() |
byteStream() |
|---|---|---|---|
| 返回类型 | String | Okio BufferedSource | JDK InputStream |
| 内置缓冲 | 有(内部临时使用) | 自带 Okio 缓冲 | 无,需手动包装 |
| 读取方式 | 一次性读完所有数据 | 手动循环分段读取 | 原生字节手动读取 |
| 内存占用 | 全量加载,大数据 OOM 风险高 | 固定小块缓冲,内存可控 | 无缓冲,频繁读性能差 |
| 连接生命周期 | 读完自动关闭 | 手动 close 才断开 | 手动 close 才断开 |
| 是否支持长连接 SSE | ❌ 会阻塞卡死 | ✅ 完美适配 | ⚠️ 可用但体验差 |
| 便捷工具方法 | 无,直接出字符串 | readUtf8Line、exhausted 等 | 只有基础 read () |
| 设计目的 | 快速获取短文本结果 | 高性能流式处理 | 兼容传统 Java IO |
四、关键原理总结
- 共性:三者数据源完全一致,底层都依赖 Okio 缓冲流从内核 TCP 缓冲区拷贝数据;
- 本质差异
string():封装了「读完全部 + 转字符串 + 关流」一站式逻辑;source():直接暴露高性能缓冲流,把读取、连接控制权交给开发者;byteStream():是source()的兼容适配器,转成老式 Java 标准流,牺牲易用性换兼容性。
- 性能优先级:
source()>byteStream()(手动加缓冲) >byteStream()(原生无缓冲)
更多推荐




所有评论(0)