Android 网络框架解压缩(gzip)浅谈

六谈这个话题,是因为很多时间都忽略了这个因素,网络传输数据的压缩很少有人去关注,然而有时间提到这个问题的时间却一时不知道怎么回答,或者已经忘掉了这个概念...

进入正题,首先来聊聊Gzip。

一、Gzip概念

Gzip是GNUZip的缩写,他是一个GNU自由软件的文件圧缩程序。

二、为什么要用Gzip

我们在进行网络传输数据时,经常用到json、xml等格式的数据,这些数据在传输前可以进行压缩,这时候就会涉及到一种压缩格式—Gzip。Gzip的压缩比率非常大,有的甚至能达到99.9%以上,可以大大减少传输内容,提高用户的传输速度,进而提高用户的体验。

三、检测是否使用Gzip压缩以及压缩比例

http://tool.chinaz.com/Gzips/

https://gzip.51240.com/

比如我们通过第一个链接看一下“开源中国的新闻页”,网址如下:

http://www.oschina.net/action/api/news_list?catalog=1&pageIndex=0&pageSize=20

压缩结果

结果显示,这个网页没有进行压缩,源文件大小为12KB,而压缩后,文件可减小到0.01KB,可以节省99.92%的传输控件。这是什么概念呢?相当于100MB的数据经过压缩后不到1MB。

四、Android中实现Gzip压缩的原理

说道这里,我们先说一下Http中的Gzip技术细节

HTTP协议上的GZIP编码是一种用来改进WEB应用程序性能的技术。一般服务器中都安装有这个功能模块的,服务器端不需做改动,当浏览器支持gzip 格式的时候, 服务器端会传输gzip格式的数据。具体讲就是 http request 头中 有 "Accept-Encoding", "gzip" ,response 中就有返回头Content-Encoding=gzip ,我们现在从浏览器上访问玩啥网站都是gzip格式传输的。

同样的的道理,我们可以在android 客户端 request 头中加入 "Accept-Encoding", "gzip" ,来让服务器传送gzip 数据。


首先,客户端发请求给服务端,会带上请求头:Accept-Encoding:gzip。第二步,服务端接收到请求头后,可以选择压缩或不压缩。第三步,服务端选择压缩后,文件明显变小,同时在响应头加上Content-Encoding:gzip。第四步,客户端接收到响应后,根据响应头中是否带有Content-Encoding:gzip,判断文件是否被压缩,如果压缩就进行解压,如果没有压缩,就按照正常方式读取数据即可。

五、在Android各网络框架中表现有什么差异

OKhttp

OKhttp3.4.0开始将这些逻辑抽离到了内置的interceptor中,看起来较为方便

BridgeInterceptor.java这个类里边可以看到

// 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)));

}

针对返回结果,如果同时满足以下三个条件:

1.transparentGzip为true,即之前自动添加了Accept-Encoding

2.header中标明了Content-Encoding为gzip

3.有body

移除 Content-Encoding、Content-Length,并对结果进行解压缩。

可以看到以上逻辑完成了,由此我们通过OkHttp源码得出以下结论:

1.开发者没有添加Accept-Encoding时,自动添加Accept-Encoding: gzip

2.自动添加的request,response支持自动解压

3.手动添加不负责解压缩

4.自动解压时移除Content-Length,所以上层Java代码想要contentLength时为-1

5.自动解压时移除 Content-Encoding

6.自动解压时,如果是分块传输编码,Transfer-Encoding: chunked不受影响。

HttpUrlConnection

由于引用太多源码就不写了,直接针对以上6点做结果分析

1.2.3后默认是gzip,不加Accept-Encoding会被自动添加上Accept-Encoding: gzip。

2.自动添加的request,response支持自动解压

3.手动添加不会负责解压缩。

4.这里提出一点HttpURLConnection 在Android 4.4以后底层是由OkHttp实现的,所以

      *4.4之后的版本,Content-Length被移除,getContentLength() = -1

      *2.3- 4.3之间,Content-Length 没有移除,getContentLength() = compressed size

5. 自动解压时的Content-Encoding

与Content-Length对应:

      *4.4之后的版本,Content-Encoding被移除

     *2.3- 4.3之间,Content-Encoding存在,无变化。

6. 自动解压时的分块编码传输

与OkHttp相同,Transfer-Encoding: chunked不受影响。

六、具体代码实例看下解压流程

private String getJsonStringFromGZIP(HttpResponse response) {

       String jsonString = null;

       try {

               InputStream is = response.getEntity().getContent();

               BufferedInputStream bis = new BufferedInputStream(is);

               bis.mark(2);

              // 取前两个字节

              byte[] header = new byte[2];

              int result = bis.read(header);

              // reset输入流到开始位置

             bis.reset();

             // 判断是否是GZIP格式

            int headerData = getShort(header);

           // Gzip 流 的前两个字节是 0x1e8b

          if (result != -1 && headerData == 0x1e8b) { LogUtil.d("HttpTask", " use GZIPInputStream  ");

                  is = new GZIPInputStream(bis);

          } else {

                  LogUtil.d("HttpTask", " not use GZIPInputStream");

                  is = bis;

         }

         InputStreamReader reader = new InputStreamReader(is, "utf-8");

         char[] data = new char[100];

         int readSize;

         StringBuffer sb = new StringBuffer();

         while ((readSize = reader.read(data)) > 0) {

                 sb.append(data, 0, readSize);

         }

                jsonString = sb.toString();

               bis.close();

               reader.close();

        } catch (Exception e) {

                LogUtil.e("HttpTask", e.toString(),e);

       }

       LogUtil.d("HttpTask", "getJsonStringFromGZIP net output : " + jsonString );

        return jsonString;

}

private int getShort(byte[] data) {

         return (int)((data[0]<<8) | data[1]&0xFF);

}

参考资料

         Android’s HTTP Clients

         HttpURLConnection

         HTTP 协议中的 Transfer-Encoding

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 194,088评论 5 459
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 81,715评论 2 371
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 141,361评论 0 319
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 52,099评论 1 263
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 60,987评论 4 355
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 46,063评论 1 272
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 36,486评论 3 381
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 35,175评论 0 253
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 39,440评论 1 290
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 34,518评论 2 309
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 36,305评论 1 326
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 32,190评论 3 312
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 37,550评论 3 298
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 28,880评论 0 17
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 30,152评论 1 250
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 41,451评论 2 341
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 40,637评论 2 335

推荐阅读更多精彩内容