Android 网络优化及protobuf
https是常用的互联网应用层协议,客户端的一次请求大致经历了:客户端获取URL - > DNS解析 - > 建立HTTP连接 - >发送HTTP请求 几个阶段。
可以发现有三个可以优化的地方:
直接使用 IP 地址,去除 DNS 解析步骤;
连接复用;
压缩数据,减小传输的数据大小。
DNS优化
DNS(Domain Name System),它的作用是根据域名查出IP地址,它是HTTP协议的前提,只有将域名正确的解析成IP地址后,后面的HTTP流程才能进行。DNS 完整的解析流程很长,会先从本地系统缓存取,若没有就到最近的 DNS 服务器取,若没有再到主域名服务器取,每一层都有缓存,但为了域名解析的实时性,每一层缓存都有过期时间。
传统的DNS解析机制有几个缺点:
- 缓存时间设置得长,域名更新不及时,设置得短,大量 DNS 解析请求影响请求速度;
- 域名劫持,容易被中间人攻击,或被运营商劫持,把域名解析到第三方 IP 地址;
- DNS 解析过程不受控制,无法保证解析到最快的IP;
针对传统DNS的缺点,HTTPDNS主要有以下优点:
- 域名防劫持
- 智能调度(获取最快最优IP)
相关文章推荐 https://mp.weixin.qq.com/s/iaPtSF-twWz-AN66UJUBDg
百度、阿里、腾讯都推出了自己的HTTPDNS方案。
百度云 https://cloud.baidu.com/doc/HTTPDNS/s/ujwvxm073
阿里云 https://help.aliyun.com/document_detail/30103.html?spm=a2c4g.11186623.6.543.665c3eabM102wL
连接复用
HTTP/2主要是为了解决现HTTP 1.1性能不好的问题才出现的。当初Google为了提高HTTP性能,做出了SPDY,它就是HTTP/2的前身,后来也发展成为HTTP/2的标准。
HTTP/2兼容HTTP 1.1,例如HTTP Method,Status code,URI以及大部分Header Fields。
HTTP/2通过以下方法减少latency,用来改进页面加载的速度,
- HTTP Header的压缩,采用的是HPack算法。
- HTTP/2的Server Push,非常重要的一个特性。
- 请求的pipeline。
- 修复在HTTP 1.x的队头阻塞问题。
- 在单个TCP连接里多工复用请求。
HTTP/2支持HTTP 1.1里的大部分use case,例如桌面浏览器、移动浏览器、Web API、Web Server、代理服务器、反向代理服务器、防火墙和CDN等。
参考博客 https://www.cnblogs.com/confach/p/10141273.html
简单来说就是客户端和服务端都使用HTTP/2
数据压缩
数据对请求速度的影响分两方面,一是压缩率,二是解压序列化反序列化的速度。目前最流行的两种数据格式是 json 和 protobuf,json 是字符串,protobuf 是二进制,即使用各种压缩算法压缩后,protobuf 仍会比 json 小,数据量上 protobuf 有优势,序列化速度 protobuf 也有一些优势 。
相比其他格式,protobuf优势有如下几点
序列化后体积相比Json和XML很小,适合网络传输
支持跨平台多语言
消息格式升级和兼容性还不错
序列化反序列化速度很快,快于Json的处理速速
PS:在一个需要大量的数据传输的场景中,如果数据量很大,那么选择protobuf可以明显的减少数据量,减少网络IO,从而减少网络传输所消耗的时间。
protobuf使用
protobuf GitHub仓库地址 https://github.com/protocolbuffers/protobuf/tree/master/java
-
添加gradle插件
classpath 'com.google.protobuf:protobuf-gradle-plugin:0.8.13'
-
应用插件
apply plugin: 'com.google.protobuf' protobuf { protoc { artifact = 'com.google.protobuf:protoc:3.11.0' } generateProtoTasks { all().each { task -> task.builtins { java { option "lite" } } } } }
-
添加依赖
implementation 'com.google.protobuf:protobuf-javalite:3.11.0'
-
在java文件夹同级目录下创建proto文件夹,并创建.proto文件
-
填写.proto 文件内容,参考语法 https://blog.csdn.net/qq_22660775/article/details/89163881
syntax = "proto3";//proto3 option java_package = "com.example.proto";//生成的java类的包名
-
编译构建,就会在生成对应的java文件
-
调用对应的java API
//构建对象 Demo2.Test test = Demo2.Test.newBuilder() .setCode("co") .setNumber(24) .setD(2.22) .setF(1.5f) .addList("sd") .putMap(15,"map12") .build(); //序列化后的长度 int size = test.getSerializedSize(); //序列化 byte[] bytes2 = test.toByteArray(); //反序列化 try { Demo2.Test demoTest2 = Demo2.Test.parseFrom(bytes2); //javalite版无法使用JsonFormat // JsonFormat.printer().print((MessageOrBuilder)demoTest2); //使用fastjson Log.d("protoString", JSON.toJSONString(demoTest2)); } catch (InvalidProtocolBufferException e) { e.printStackTrace(); }
比较遗憾的是javalite版无法使用JsonFormat,曾经尝试把protobuf-java-util中的类拷贝下来,但由于util包依赖太多,最终放弃了
使用fastjson打印结果中数据,虽然内容变多了,但核心内容都在,可以勉强使用。
{ "code": "co", "codeBytes": { "empty": false, "validUtf8": true }, "d": 2.22, "defaultInstanceForType": { "code": "", "codeBytes": { "empty": true, "validUtf8": true }, "d": 0, "defaultInstanceForType": { "$ref": "@" }, "f": 0, "initialized": true, "listCount": 0, "listList": [], "map": {}, "mapCount": 0, "mapMap": {}, "number": 0, "parserForType": {}, "serializedSize": 0 }, "f": 1.5, "initialized": true, "listCount": 1, "listList": [ "sd" ], "map": { "15": "map12" }, "mapCount": 1, "mapMap": { "15": "map12" }, "number": 24, "parserForType": { "$ref": "$.defaultInstanceForType.parserForType" }, "serializedSize": 35 }
-
推荐Android studio 安装两个插件
protocol-buffer-editor 插件(语法高亮)
pojo to proto 插件(拷贝java bean文件,生成proto文件的内容)
其他
- 根据网络情况下发图片,在2G/3G/4G/5G/WIFI下分别获取不同分辨率的图片
- 开启数据压缩(okhttp默认支持接收gzip压缩)
- 根据网络情况下发图片,在2G/3G/4G/5G/WIFI下分别获取不同分辨率的图片
- WIFI下,按需提前上传下载大数据包
- 使用长连接保活时考虑智能心跳包时间间隔