前言:为什么网络优化比别人慢?优化那些还不够?应该加强哪里?
1.Android应用为什么需要考虑网络优化,主要基于以下几个原因:
- 流量:App的移动网络流量消耗对用户来说是比较敏感的;
- 电量:网络请求对电量的消耗是比较大的,影响手机的待机时间;
- 用户体验:网络请求是耗时操作,如果App请求等待时间长,会给用户网络卡, 应用反应慢的感觉。
网络请求优化的目的是尽可能地减少用户等待的时间、减少用户的流量使用、减少对手机电量的消耗,最终达到提升用户体验。
2.网络性能优化主要从如下几个方面着手:
- 流量消耗
- 网络速度
- 获取数据优化
- 接口设计
- 弱网优化
一丶流量消耗优化
所谓流量优化,就是在保证数据正确的前提下,如何保证尽可能地少消耗用户流量。虽然现在的流量费用越来越便宜,但是减少流量本身并不是最重要的目的,而是提升用户体验带来的结果。
1.使用本地缓存,减少网络请求
分为几种情况:
- 基本数据:可以使用持久化技术,比如本地数据库,sharedPreference,本地文件,来记录之前请求下来的数据。而且具有一定的过期时效来保证数据的可用性。
- 图片:同样的,在本地缓存和此图片url映射的图片,可以避免图片的网络请求。目前很多优秀的图片加载框架,如Glide,Picasso,Fresco都具备这种能力。
- 文件:如一般的文件,webview里的js,css,html等,当然也要注意过期时效。
2.合理合并api接口,减少网络请求
这种方案可以使得多次网络请求合并为一次,减少网络连接来回往返的等待时长。
比如有一种场景:App 本地数据库缓存有N个表,当老用户卸载重装时,首次检查需要把远端的数据一次性download到本地来,保证两端的一致性。
由于之前必然为每个表设计了一个api,但是在这种场景下,如果还是继续使用这些api的话,就需要N次网络请求,结果不仅会造成N次网络I/O和磁盘写入I/O,还会使得服务端查找性能降低,用户等待时间较长,本地磁盘写入性能降低。
针对这种场景,完全可以设计一个bulk sync api把这些api整合为一个,这样一来客户端只需一次网络I/O即可获取所有数据,服务端查找性能也提升了一个数量级,本地磁盘I/O也可以把此次返回的数据当成一次事务一次性插入数据库中,大大减少了用户等待时间,同时也提升了网络和本地磁盘写入性能。
3.减少网络请求的数据量
当网络请求不可避免的情况下,此时就要考虑如何减少网络请求携带的数据量。
同样也分几种情况:
- 基本数据:使用数据压缩方式,比如protoBuff格式,okhttp开启gzip压缩,支持Http 2.0,使用其头部压缩,二进制格式替代Http 1.1 文本格式的特性。
- 图片:当不得不获取一张本地缓存没有的图片时,就不得不请求服务端获取。此时没有必要使用最高清的大尺寸的原始图片,完全可以针对客户端显示的图片尺寸和屏幕密度,向服务器请求较小的尺寸,和较低质量的图片,服务器通过图片处理,返回符合此参数的图片给客户端进行显示。
- 文件:普通文件可以进行zip打包下发,而如webview需要加载的css,js,html还可以事先使用webpack压缩,然后再zip打包下发。
二.丶速度优化
在进行网络流量优化的同时;由于网络请求次数;请求携带的数据量变少了;这其实也是在变相地提高网络速度。
另外,还有其他一些方式:
- 如okhttp中复用网络连接的方法,当网络请求等待队列中的host和之前的某个结束的请求连接host一致,此时可以直接让此请求复用此网络连接。
- 使用http 2.0的多路复用特性,我们都知道在http 2.0中,使用了stream的概念,使得多个网络请求可以并行发送,不再需要等待上一次请求响应回来再发送下一个请求,从而有效避免了网络请求的排队,提升了整体网络请求速度。
- 当有网络数据需要具备一定的实时性,可以使用长连接方式,比如websocket,http 2.0的方式进行推送,避免通过网络轮询的方式来达到,这样不仅实时性高,而且减少了网络请求次数,提升了用户体验。
三丶获取数据优化
- 连接复用:节省连接时间,如开启keep-alive。Android中HttpUrlConnection默认是开启的。
- 请求合并:可以将多个请求合并为一个进行请求。
- 减小请求数据,并压缩:对于post请求,Body可以做Gzip压缩,如日志。
- 精简数据格式:对于后台返回的数据,尽量使用Json来代替XML。
- 数据的增量更新
四丶接口设计
1.API设计
App与Server之间的API设计要考虑网络请求的频次, 资源的状态等. 以便App可以以较少的请求来完成业务需求和界面的展示。
- 例如, 注册登录. 正常会有两个API, 注册和登录, 但是设计API时我们应该给注册接口包含一个隐式的登录. 来避免App在注册后还得请求一次登录接口(有可能失败, 从而导致业务流程失败)
- 再例如, 上文提到的获取repo详情, 实际上请求了4个接口, 请求了repo的信息, forks列表, contributors列表, readme, 这是因为github提供的接口是尽量单一职责的. 然而在我们的实际开发中, 我们的Server除了提供这些单一职责的小接口外, 最好还能组合一个满足客户端业务需求的repo详情接口出来。
2.Gzip压缩
使用Gzip来压缩request和response, 减少传输数据量, 从而减少流量消耗。
考虑使用Protocol Buffer代替JSON
从前我们传输数据使用XML, 后来使用JSON代替了XML, 很大程度上也是为了可读性和减少数据量(当然还有映射成POJO的方便程度)。
Protocol Buffer是google推出的一种数据交换格式。
如果我们的接口每次传输的数据量很大的话, 可以考虑下protobuf, 会比JSON数据量小很多。当然相比来说, JSON也有其优势, 可读性更高。
3.图片的Size
上.NETwork Monitor中看到的22s到27s之间的有多次请求, 且数据量还很大. 就是在获取图片资源。图片相对于接口请求来说, 数据量要大得多. 故而也是我们需要优化的一个点。
我们可以在获取图片时告知服务器需要的图片的宽高, 以便服务器给出合适的图片, 避免浪费。现在很多公司的图片资源都是使用第三方的云存储服务的(七牛, 阿里云存储之类的)
以七牛为例, 可以在请求图片的url中添加诸如质量, 格式, width, height等path来获取合适的图片资源:
五丶弱网优化
1.弱网标准
百度其中就有使用的是一个循序渐进的过程,通过三个阶段来建立一个弱网标准。
- 第一阶段,线下测试:
- 获取预期的阈值,在测试App网络切换、DNS故障时,抓取这些数据
- 第二阶段,线上进行验证:
- 通过线下获取到的阈值,在线上可以获取到弱网的比例,找到会出现弱网的场景,进行诊断优化。(比如网络体验最好的微信,也只是在信令的传输上优化到极致)
- 第三阶段,线上反复试验:
- 通过反复试验来调整阈值,使得优化更接近业务层。
- 腾讯的做法也大同小异,都是根据2.2的指标进行数据收集,整理一套达到弱网时的数据标准。但是我们该如何去探测这些数据呢?这需要我们实现一套完整的网络探测架构。
2.弱网检测
网络探测是弱网检测的基础,目标是能够即时、正确的检测出网络质量,我们把网络探测分成两个部分,主动网络探测和被动网络采集。
3.主动网络探测
主动检测,就是在触发了某些特定条件后,主动的进行网络检测,并按照一定的条件检查出是否为弱网状态。架构图如下所示:
4.弱网优化
利用工具模拟弱网, 在弱网情况下体验我们的App. 一般来说, 网络延迟在60ms内, 是OK的, 超过200ms就比较糟糕了. 我们需要做的是在比较糟糕的网络环境下还能给用户较好的体验。
弱网优化, 本质上是在弱网的情况下能让用户流畅的使用我们的App. 我们要做的就是结合上述的优化项:
- 压缩/减少数据传输量
- 利用缓存减少网络传输
- 针对弱网(移动网络), 不自动加载图片
- 界面先反馈, 请求延迟提交 例如, 用户点赞操作, 可以直接给出界面的点赞成功的反馈, 使用JobScheduler在网络情况较好的时候打包请求
五丶结尾
网络优化,是App优化中相当重要的一项优化. 除了客户端, 接口的优化外, 很多一部分优化还依赖于服务器端, 包括服务器端的代码开发, 部署方式等。看到这里想必大家心里应该都知道,应该学习哪块;哪里学习不够;和别人的区别在哪吧。
自己也是从事Android开发5年有余了;整理了一些Android开发技术核心笔记和面经题纲,如有需要的同学请私信我回复“核心笔记”或“面试”领取!