网络优化:HTTP/2与连接池、OkHttp拦截器链、DNS优化、数据压缩、弱网环境适配
大家好,我是你们的老朋友。今天我们来聊聊网络优化。说实话,网络这块水挺深的。我见过太多App,功能做得不错,但一进页面就转圈圈,用户早跑了。网络优化,说白了就是让数据更快、更稳地到达用户手里。
我个人习惯把网络优化分成五个维度:协议层面、连接复用、请求拦截、数据压缩、弱网适配。咱们一个一个来拆解。
1. HTTP/2 与连接池:别再让连接排队了
先问大家一个问题:HTTP/1.1 有什么毛病?
嗯,最大的问题就是队头阻塞。一个连接上只能串行发请求,前一个没回来,后面的就得等着。虽然可以开多个连接(通常是6个),但每个连接都要三次握手、TLS握手,成本很高。
HTTP/2 解决了这个问题。它引入了多路复用,一个连接上可以同时跑多个请求。说白了,就是一条高速公路上可以并排跑很多辆车,不用一辆一辆排队了。
我在项目中遇到过这样一个场景:一个页面需要同时加载10张小图,用HTTP/1.1的时候,浏览器最多开6个连接,剩下的4张得等。换成HTTP/2之后,所有图片几乎同时到达,加载速度提升了40%以上。
但光有HTTP/2还不够,你还得配合连接池。OkHttp默认就支持连接池,它会复用已经建立的连接,避免重复握手。
// OkHttp 连接池配置示例
ConnectionPool connectionPool = new ConnectionPool(
5, // 最大空闲连接数
5, // 保持存活时间
TimeUnit.MINUTES
);
OkHttpClient client = new OkHttpClient.Builder()
.connectionPool(connectionPool)
.protocols(Arrays.asList(Protocol.HTTP_2, Protocol.HTTP_1_1))
.build();
2. OkHttp 拦截器链:请求的"流水线"
OkHttp 的拦截器链,说白了就是一个请求处理流水线。每个拦截器负责一件事,比如加Header、打日志、重试、缓存等。
拦截器分两种:应用拦截器和网络拦截器。应用拦截器在请求发出前和响应回来后各执行一次,网络拦截器在真正的网络请求前后执行。
我个人习惯用网络拦截器来做重试和日志。为什么呢?因为网络拦截器能拿到真正的网络请求信息,比如IP地址、TLS版本等。
// 自定义重试拦截器
class RetryInterceptor implements Interceptor {
private static final int MAX_RETRY = 3;
@Override
public Response intercept(Chain chain) throws IOException {
Request request = chain.request();
Response response = null;
IOException lastException = null;
for (int i = 0; i < MAX_RETRY; i++) {
try {
response = chain.proceed(request);
if (response.isSuccessful()) {
return response;
}
} catch (IOException e) {
lastException = e;
// 等待一段时间再重试
try {
Thread.sleep(1000 * (i + 1));
} catch (InterruptedException ignored) {}
}
}
throw lastException != null ? lastException : new IOException("重试失败");
}
}
3. DNS 优化:别让域名解析拖后腿
DNS解析,很多人不重视。但你想想看,每次请求都要先查一下域名对应的IP,如果DNS服务器慢,或者被劫持了,那体验就很糟糕了。
我曾经遇到过一个问题:某个地区的用户反馈App加载慢,查了半天发现是当地DNS服务器响应慢,每次解析要2-3秒。后来我们做了DNS预解析和HTTPDNS,问题就解决了。
DNS预解析很简单,就是在App启动时提前把常用域名的IP解析好,缓存起来。
// OkHttp 自定义 DNS 解析
class CustomDns implements Dns {
private final Map<String, List<InetAddress>> cache = new ConcurrentHashMap<>();
@Override
public List<InetAddress> lookup(String hostname) throws UnknownHostException {
List<InetAddress> cached = cache.get(hostname);
if (cached != null) {
return cached;
}
// 使用 HTTPDNS 或自定义解析
List<InetAddress> addresses = Dns.SYSTEM.lookup(hostname);
cache.put(hostname, addresses);
return addresses;
}
}
HTTPDNS 就更高级了,它直接用HTTP请求去获取IP,绕过了本地DNS,能有效避免DNS劫持。像阿里云、腾讯云都有现成的HTTPDNS服务。
4. 数据压缩:让传输更轻量
数据压缩,说白了就是让要传输的数据变小。常用的方式有两种:Gzip 和 Protobuf。
Gzip 是文本压缩的利器。JSON 数据经过 Gzip 压缩后,体积能减少 60%-80%。OkHttp 默认就支持 Gzip 解压,你只需要在请求头里加上 Accept-Encoding: gzip 就行。
但 Gzip 有个问题:它是在传输层面压缩的,数据到了客户端还得解压。如果数据量特别大,解压也会消耗CPU。
Protobuf 就不一样了。它是结构化数据序列化的格式,比 JSON 更小、更快。我做过对比:同样的数据,JSON 是 1KB,Protobuf 只有 300B 左右。
// Protobuf 定义示例
syntax = "proto3";
message User {
int32 id = 1;
string name = 2;
string email = 3;
}
// 序列化
User user = User.newBuilder()
.setId(1001)
.setName("张三")
.setEmail("zhangsan@example.com")
.build();
byte[] data = user.toByteArray(); // 只有几十个字节
5. 弱网环境适配:别让用户干等
弱网环境,是每个Android开发者都绕不开的坎。地铁里、电梯里、偏远地区,网络质量都很差。这时候如果App还傻傻地等请求回来,用户早就关掉了。
我总结了几条弱网适配的经验:
- 超时设置要合理:默认的超时时间往往太长。我一般设成 10秒连接超时、15秒读取超时。
- 使用缓存:能缓存的数据尽量缓存,弱网时直接展示缓存内容。
- 请求合并:把多个小请求合并成一个,减少连接次数。
- 图片降级:弱网时加载低分辨率图片,甚至只加载文字。
// 弱网环境下的超时配置
OkHttpClient client = new OkHttpClient.Builder()
.connectTimeout(10, TimeUnit.SECONDS) // 连接超时
.readTimeout(15, TimeUnit.SECONDS) // 读取超时
.writeTimeout(10, TimeUnit.SECONDS) // 写入超时
.retryOnConnectionFailure(true) // 连接失败重试
.build();
还有一个技巧:检测网络状态。当网络从WiFi切到移动数据,或者信号变弱时,主动调整请求策略。
// 网络状态监听
ConnectivityManager cm = (ConnectivityManager) context.getSystemService(Context.CONNECTIVITY_SERVICE);
NetworkCallback callback = new NetworkCallback() {
@Override
public void onAvailable(Network network) {
// 网络恢复,正常请求
}
@Override
public void onLost(Network network) {
// 网络断开,停止请求
}
@Override
public void onCapabilitiesChanged(Network network, NetworkCapabilities capabilities) {
// 网络质量变化,调整策略
boolean isMetered = !capabilities.hasCapability(NetworkCapabilities.NET_CAPABILITY_NOT_METERED);
if (isMetered) {
// 移动网络,降低图片质量
}
}
};
cm.registerNetworkCallback(new NetworkRequest.Builder().build(), callback);
知识体系总览
下面这张图,是我整理的网络优化知识体系。你可以把它当成一个检查清单,看看自己还有哪些没做到位。
好了,网络优化这块就聊到这儿。记住一句话:网络优化不是一蹴而就的,而是一个持续迭代的过程。每次上线前,都问问自己:弱网下能跑吗?数据够小吗?连接够快吗?