进入主题之前,我们先来看一下客户端与服务器通信过程中,如果服务器支持,HTTP gzip 压缩是如何实现的?
如图所示:
request header 中声明
,告知服务器客户端接受 gzip 的数据。 服务器支持的情况下,返回 gzip 后的 response body,同时加入以下 header:
- Accept-Encoding: gzip
:表明 body 是 gzip 过的数据
- Content-Encoding: gzip
:表示 body gzip 压缩后的数据大小,便于客户端使用。
- Content-Length:117
:
- Transfer-Encoding: chunked
OK,HTTP gzip 压缩的基本流程我们理清楚了,来看在 Android 各网络框架中表现有什么差异。
OkHttp 作为目前 Android 最火的网络库,应用范围较广,相比于 Android 自带的 HttpUrlConnection、Apache 坑也少很多。
我们首先来看这个库的实现:
(注:以下代码基于 OkHttp 3.4.1, 之前的版本逻辑也是一样的,但 3.4.0 开始将这些逻辑抽离到了内置的 interceptor 中,看起来较为方便)
- // If we add an "Accept-Encoding: gzip" header field we're responsible for also decompressing
- // the transfer stream.
- boolean transparentGzip = false;
- if (userRequest.header("Accept-Encoding") == null) {
- transparentGzip = true;
- requestBuilder.header("Accept-Encoding", "gzip");
- }
如果 header 中没有
,默认自动添加 ,且标记变量
- Accept-Encoding
为
- transparentGzip
。
- true
- if (transparentGzip && "gzip".equalsIgnoreCase(networkResponse.header("Content-Encoding")) && HttpHeaders.hasBody(networkResponse)) {
- GzipSource responseBody = new GzipSource(networkResponse.body().source());
- Headers strippedHeaders = networkResponse.headers().newBuilder().removeAll("Content-Encoding").removeAll("Content-Length").build();
- responseBuilder.headers(strippedHeaders);
- responseBuilder.body(new RealResponseBody(strippedHeaders, Okio.buffer(responseBody)));
- }
针对返回结果,如果同时满足以下三个条件:
为
- transparentGzip
,即之前自动添加了
- true
- Accept-Encoding
为 gzip
- Content-Encoding
移除
、
- Content-Encoding
,并对结果进行解压缩。
- Content-Length
可以看到以上逻辑完成了:
时,自动添加
- Accept-Encoding
- Accept-Encoding: gzip
,所以上层 Java 代码想要 contentLength 时为 - 1
- Content-Length
- Content-Encoding
不受影响。
- Transfer-Encoding: chunked
以上 6 点是我们通过 OkHttp 源码得出的结论,我们以此来继续看下其他框架。
- Accept-Encoding: gzip
官网有过说明:
In Gingerbread, we added transparent response compression. HttpURLConnection will automatically add this header to outgoing requests, and handle the corresponding response:
Accept-Encoding: gzip
Take advantage of this by configuring your web server to compress responses for clients that can support it. If response compression is problematic, the shows how to disable it.
即:2.3 后默认是 gzip,不加
会被自动添加上
- Accept-Encoding
。
- Accept-Encoding: gzip
By default, this implementation of HttpURLConnection requests that servers use gzip compression and it automatically decompresses the data for callers of getInputStream().
即返回的数据是已经自动解压缩的。
By default, this implementation of HttpURLConnection requests that servers use gzip compression and it automatically decompresses the data for callers of getInputStream(). The Content-Encoding and Content-Length response headers are cleared in this case. Gzip compression can be disabled by setting the acceptable encodings in the request header:
- urlConnection.setRequestProperty("Accept-Encoding", "identity");
Setting the Accept-Encoding request header explicitly disables automatic decompression and leaves the response headers intact; callers must handle decompression as needed, according to the Content-Encoding header of the response.
例子中只提到设置为
时可以禁止 gzip 压缩。 但是请注意最后一段提到,显式声明会禁止自动解压,同时保留 header 完整性,需要根据
- identity
来自己处理 response。
- Content-Encoding
实测 4.1 - 6.0 版本之后发现,并不是非要指定 identity 才能屏蔽,指定 gzip 一样也不会解压缩。so,只要是显式声明过,都不会再处理,即:手动添加不会负责解压缩。
Since HTTP's Content-Length header returns the compressed size, it is an error to use getContentLength() to size buffers for the uncompressed data. Instead, read bytes from the response until InputStream.read() returns -1.
即:
值为 gzip 压缩时的数据大小。
- getContentLength()
之前提到 OkHttp 在处理 gzip 压缩时会把
移除,contentLength 在 Java 层获取为 - 1,而 HttpURLConnection 在 Android 4.4 以后底层是由 OkHttp 实现的,那文档中提到的
- Content-Length
是 compressed size 是否还继续成立呢?
- getContentLength()
实测后发现 :
被移除,
- Content-Length
= -1
- getContentLength()
没有移除,
- Content-Length
= compressed size
- getContentLength()
与 Content-Length 对应:
被移除
- Content-Encoding
存在,无变化。
- Content-Encoding
与 OkHttp 相同,
不受影响。
- Transfer-Encoding: chunked
这里不再赘述,仅阐述结论:
无自动添加、解压机制。
1、是否支持自动添加
与数据自动解压?
- Accept-Encoding
name | transparent response compression |
---|---|
OkHttp | yes |
HttpUrlConnection | yes |
Apache | no |
2、支持自动后,response header 的表现如何?
name | Content-Encoding: gzip | Header : Content-Length | Java : ContentLength |
---|---|---|---|
OkHttp | 被移除 | 被移除 | -1 |
HttpUrlConnection(2.3 ~ 4.3) | 不变 | 不变 | compressed size |
HttpUrlConnection(4.4 ~ ?) | 被移除 | 被移除 | -1 |
或
name | Content-Encoding: gzip | Transfer-Encoding: chunked |
---|---|---|
OkHttp | 被移除 | 不变 |
HttpUrlConnection(2.3 ~ 4.3) | 不变 | 不变 |
HttpUrlConnection(4.4 ~ ?) | 被移除 | 不变 |
3、HttpUrlConnection、OkHttp 均是手动添加不自动解压缩,Apache 没有自动添加自动解压功能。三者在手动添加
后,表现一致(利用这个特点,可以做一个在三者之上的网络框架,随意切换三种通道)。
- Accept-Encoding
来源: http://www.cnblogs.com/ct2011/p/5835990.html