29、网络请求优化:OkHttp连接池、Gzip压缩、数据预取、WebSocket vs HTTP
网络请求,说白了就是App跟服务器之间的“对话”。对话效率高不高,直接影响用户体验。我见过不少App,功能做得挺好,就是卡在网络请求上——转圈圈、加载慢、流量跑得飞快。用户没耐心等,直接卸载了。
今天咱们聊聊四个核心优化点:OkHttp连接池、Gzip压缩、数据预取、WebSocket vs HTTP。这些技术我都在项目里用过,踩过坑,也总结了一些经验。
一、OkHttp连接池:别让TCP握手拖慢你
每次HTTP请求,底层都要经历TCP三次握手。如果频繁请求,握手开销会非常大。OkHttp的连接池就是干这个的——复用已有的TCP连接,避免重复握手。
核心思路:连接池维护一组“活着的”TCP连接,新请求来了,优先从池子里拿。用完了不立即关闭,而是放回池子,等下一个请求复用。
我在项目中遇到过一个问题:某个页面需要同时请求5个接口,结果每个接口都新建连接,耗时翻倍。后来用OkHttp连接池,5个请求复用同一个连接,速度提升明显。
OkHttp连接池配置
OkHttpClient client = new OkHttpClient.Builder()
.connectionPool(new ConnectionPool(5, 30, TimeUnit.SECONDS))
.build();
参数说明:
- maxIdleConnections:最大空闲连接数。我一般设5~10,太多浪费内存,太少复用率低。
- keepAliveDuration:空闲连接存活时间。默认5分钟,我习惯设30秒到1分钟。为什么?因为移动网络不稳定,连接太久不活跃,很可能已经断了。
小技巧:如果你用Retrofit,它底层默认就是OkHttp,连接池已经配好了。但默认参数不一定适合你的场景,建议根据业务调整。
连接池工作原理
我画了一张图,帮你理解连接池的流转逻辑:
注意:连接池不是越大越好。我曾经把maxIdleConnections设成50,结果内存占用飙升,反而拖慢了整体性能。移动端建议5~10个就够了。
二、Gzip压缩:让数据“瘦身”
网络传输的数据,说白了就是一堆字符串。如果数据量大,传输时间就长。Gzip压缩可以把数据体积缩小60%~80%,效果立竿见影。
我记得有个项目,接口返回的JSON有200KB,用户每次打开都要等2秒。开启Gzip后,数据压缩到40KB,加载时间降到0.5秒。用户反馈明显变快了。
OkHttp开启Gzip
OkHttpClient client = new OkHttpClient.Builder()
.addInterceptor(new GzipInterceptor())
.build();
实际上,OkHttp默认就支持Gzip解压。你只需要在请求头里加一句:
Request request = new Request.Builder()
.url("https://api.example.com/data")
.header("Accept-Encoding", "gzip")
.build();
服务器收到这个头,如果支持Gzip,就会返回压缩后的数据。OkHttp会自动解压,你拿到的就是原始数据。
经验之谈:Gzip对文本类数据(JSON、XML、HTML)效果极好。但对图片、视频这类二进制数据,压缩效果有限,甚至可能变大。别盲目全量压缩。
什么时候不该用Gzip?
- 数据量很小(小于1KB),压缩反而增加CPU开销
- 实时性要求极高的场景(比如音视频流),压缩解压会引入延迟
- 服务器不支持Gzip,加了头也没用
三、数据预取:提前加载,用户无感知
用户还没点,数据就已经准备好了。这就是预取的核心思想。说白了,就是“猜用户下一步想干什么”。
我做过一个新闻App,用户浏览列表时,后台悄悄预取前3篇文章的详情。等用户点进去,内容瞬间展示,几乎没有加载感。用户反馈说“这App真快”。
预取策略
| 策略 | 说明 | 适用场景 |
|---|---|---|
| 列表预取 | 滑动列表时,提前加载下一页数据 | 新闻Feed、商品列表 |
| 详情预取 | 用户点击前,提前加载详情页内容 | 文章详情、商品详情 |
| 定时预取 | 每隔一段时间,后台刷新数据 | 天气、股票行情 |
| 网络空闲预取 | WiFi或4G空闲时,悄悄拉取数据 | 离线阅读、缓存更新 |
避坑指南:我曾经在预取时没控制好并发,结果一次预取了20个接口,直接把用户流量跑光了。预取一定要考虑流量和电量消耗,建议只在WiFi下做大量预取。
预取实现示例
// 使用OkHttp的预取机制
Request request = new Request.Builder()
.url("https://api.example.com/next-page")
.build();
// 预取请求,优先级低,不阻塞主线程
client.newCall(request).enqueue(new Callback() {
@Override
public void onResponse(Call call, Response response) {
// 缓存到本地,等用户真正需要时直接读取
cache.put("next_page", response.body().string());
}
@Override
public void onFailure(Call call, IOException e) {
// 预取失败没关系,用户真正请求时再加载
}
});
四、WebSocket vs HTTP:选对协议,事半功倍
HTTP和WebSocket,就像写信和打电话的区别。HTTP是“一问一答”,每次都要建立连接。WebSocket是“长连接”,建立后可以随时推送数据。
我做过一个聊天App,一开始用HTTP轮询,每3秒发一次请求查新消息。结果服务器压力巨大,用户手机电量也掉得快。后来换成WebSocket,服务器有新消息直接推过来,省电又省流量。
什么时候用HTTP?
- 请求-响应模式,比如获取文章列表、提交表单
- 数据更新不频繁,比如每天只刷新一次
- 兼容性要求高,HTTP几乎无处不在
什么时候用WebSocket?
- 实时性要求高,比如聊天、游戏、股票行情
- 服务器主动推送,比如通知、告警
- 频繁交互,比如协作编辑、在线文档
WebSocket vs HTTP 对比
| 特性 | HTTP | WebSocket |
|---|---|---|
| 连接方式 | 短连接,每次请求新建 | 长连接,建立后复用 |
| 通信模式 | 客户端发起,服务器响应 | 双向,双方都可主动发 |
| 实时性 | 低(需要轮询) | 高(服务器主动推送) |
| 资源消耗 | 高(频繁建连、断连) | 低(长连接复用) |
| 适用场景 | 常规API请求 | 实时通信、推送 |
我的建议:别盲目上WebSocket。如果业务只是偶尔拉取数据,HTTP完全够用。WebSocket虽然实时性好,但维护长连接需要心跳、重连机制,复杂度高不少。
WebSocket在Android中的使用
// OkHttp也支持WebSocket
OkHttpClient client = new OkHttpClient.Builder()
.readTimeout(0, TimeUnit.MILLISECONDS) // 长连接不超时
.build();
Request request = new Request.Builder()
.url("wss://socket.example.com/chat")
.build();
WebSocket ws = client.newWebSocket(request, new WebSocketListener() {
@Override
public void onOpen(WebSocket webSocket, Response response) {
// 连接建立成功
webSocket.send("Hello Server!");
}
@Override
public void onMessage(WebSocket webSocket, String text) {
// 收到服务器推送的消息
Log.d("WebSocket", "收到消息: " + text);
}
@Override
public void onFailure(WebSocket webSocket, Throwable t, Response response) {
// 连接失败,需要重连
reconnect();
}
});
注意:WebSocket连接需要心跳保活。我一般每30秒发一个ping包,如果连续3次没收到pong,就判定连接断开,触发重连。另外,App切到后台时记得关闭WebSocket,省电省流量。
总结一下
网络请求优化,说白了就是四个字:省、快、稳、准。
- 连接池:省去重复握手的开销
- Gzip压缩:让数据体积变小,传输更快
- 数据预取:提前加载,用户无感知
- WebSocket:实时场景下,比HTTP更稳更准
这些技术我都踩过坑,也尝过甜头。你想想看,用户打开App,数据秒出,体验自然好。反过来,转圈圈转个没完,用户早跑了。
嗯,今天就聊到这儿。希望这些经验对你有帮助。
公众号:蓝海资料掘金营,微信deep3321