优化下载来提高网络使用效率

作者: rain 分类: 移动 发布时间: 2012-03-30 13:51 6 条评论

使用无线信号传输数据有可能使您的程序变为电量杀手的原因之一。要最小化您的程序在使用网络中所消耗的电量,您需要理解程序的连接模型如何影响底层的无线信号硬件。

这节内容将介绍无线信号状态机来解释连接模式对其的影响。由此得出一些在数据传输过程中减少电量消耗所使用的技术手段,包括 预读取和捆绑要传输的数据。

无线信号状态机

一个全功率的无线传输信号将消耗大量的电量,所以在空闲的时候无线信号将转换的不同的活动状态来减少对电量的消耗,同时还要考虑如何减少再次使用无线信号时所需要的充电延时时间。

一个典型的3G无线信号通常包含3种活动状态:

  1. 全功率: 这种状态下网络连接是激活的,并且允许设备使用最大的传输速率来传输数据
  2. 低功率: 只使用全功率状态下50%左右电量的中间状态
  3. 待命状态: 当无需网络连接或者没有激活的网络连接情况下的最不活跃的状态,该状态消耗最少的电量。

很明显,低功率或者待命(空闲)状态消耗的电量比较少,但是他们增加了使用网络连接的延时时间。从低功率的状态转换到全功率状态大概需要1.5秒,而从空闲状态到全功率状态甚至需要2秒或者更多时间。

为了减少延时,状态机使用了一种延迟手段来推迟状态之前的切换。图一演示了一种在 AT&T 网络中3G信号的切换策略

图一 典型的3G无线信号状态机

每个设备上的信号状态机都具有转换推迟和启动延时,不同的无线信号(例如 3G、2G、LTE等)以及不同的运营商都有不同的推迟时间设置。

这里介绍了一种在3G无线信号中具有代表性的状态机,详情可以参考AT&T提供的数据。但是这些基本原则和最佳实践对于所有的无线信号类型都是适用的。

这种方式影响最广的就是传统的浏览器,当用户浏览网页的时候用来减少令人郁闷的延时时间。使用小的转换推迟时间可以确保当浏览会话完成的时候,无线信号就可以很快的切换到低功率状态了。

不幸的是,这种方式 可能导致在现代智能系统(例如 android)上使用起来效率很低。在Android系统中前台程序和后台程序对延时的要求也是不一样的,前台程序应该减少延时而后台程序应该减少电量的消耗。

程序如何影响无线信号状态机

每当您创建了一个网络连接,无线信号就转换到全功率状态。在上面所描述的3G信号状态机中,这种全功率状态将保持您传输数据需要的时间再加上额外的5秒转换推迟时间,然后是12秒的低功率状态。所以每次数据传输将导致无线信号使用差不多20秒的电量。

在实际操作中,这就意味着如果一个程序每隔18秒传输1秒的零散数据将导致无线信号一直处于活跃状态,当无线信号即将进入空闲状态的时候又被再次激活到全功率状态。最后的结果就是:没分钟将导致无线信号在全功率下工作18秒同时在低功率下工作42秒。

相比之下,同样的程序没分钟请求3秒捆绑的数据将使用仅仅8秒的全功率状态和12秒的低功率状态。

第二种情况可以让无线信号每分钟空闲40秒,这样减少了大量的电量消耗。

图二 捆绑的数据请求和零散的数据请求使用的无线信号电量情况

预读取数据

预读取数据是用来减少独立数据请求的一种很有效的方法。预读取数据允许您一次下载所有近期需要使用的数据。

通过预读取数据,减少了下载数据对无线信号连接的使用次数。这样不仅仅节省了电量消耗同时也减少了程序数据延时、减低了对带宽的需求以及减少了下载的次数。

预读取数据还通过减少程序内的操作延时改进了用户体验,这些延时通常都是在等待数据下载完成。

然而,过度的使用预读取数据也有不好的方面,由于存在下载的数据没有被使用的情况,这样就增加了过多消耗电量以及带宽 — 甚至超出了用户的流量限额 。而且要保证预读取数据不会使程序启动的时间延长。在实际情况下可能需要持续不断的处理数据,或者先下载程序启动需要的优先数据然后再持续的下载和处理其他数据。

是否过度的使用了预读取技术和您要下载的数据大小以及这些数据被使用的可能性。一般来说,基于上述的无线信号状态机,如果数据在当前的用户会话中被使用的几率超过50%,您就可以预读取6秒(大约1-2Mb)的数据,这样就能在预读取这些数据所消耗的电量(当这些数据没有被使用)和这些数据被使用时所需要的能量之间取得平衡。

一般来说,对于如下情况来说预读取都是好的选择:
在预读取数据后你仅仅在每隔2-5分钟后去启动另外一次下载并且每次下载1-5Mb的数据。

按照上面的原则,大数据下载–例如 下载视频文件– 应该每隔一段时间(2-5分钟)去下载一段数据,仅仅预读取即将使用到的数据。

需要注意的是:更进一步的下载应该是捆绑式(bundled)的,在下一部分将会介绍该内容,并且批量下载的数据根据不同的连接类型和速度每次下载的数据大小也会不同。

下面来看一些实际中的示例:

音乐播放器

您可以选择预下载整张专辑,但是如果用户听完了第一首歌就选择停止了,这样您就浪费了大量的流量和电量用来下载剩余的歌曲。

一种更好的方式是只缓存下一首歌曲。对于流媒体音乐来说,可以使用HTTP live 流来一次性快速的下载一首歌的数据,然后再播放,而不是在播放的过程中一直占用网络连接。

新闻阅读器

为了减少带宽消耗很多新闻阅读器都只在用户选择一个分类后才会去下载该分类下所有新闻的标题,当用户去读新闻的时候才去下载本篇报道,并且当用户滑动到新闻位置的时候才会去下载缩略图(类似于Google Play电子市场中显示程序图标的情况)。

通过这种方式,当用户在阅读过程中(滚动新闻标题、切换新闻分类以及阅读新闻报道)的大部分情况下都会激活无线信号。当切换分类或者阅读新闻的情况下由于状态之间切换的时间延时可能会导致更加糟糕的情况。

一种更好的方式是在程序启动的时候先预抓取一些合理的数据,例如 先抓取第一批新闻标题和新闻报道缩略图 — 这样可以确保程序的启动延时时候比较短 — 在程序启动后继续下载标题和缩略图,然后同时下载主要新闻的报道内容。
A better approach would be to prefetch a reasonable amount of data at startup, beginning with the first set of news headlines and thumbnails—ensuring a low latency startup time—and continuing with the remaining headlines and thumbnails, as well as the article text for each article available from at least the primary headline list.

另外一种情况可以预下载所有的新闻标题、缩略图、报道文本内容甚至包含报道中的图片 — 经常在后台预先安排好的更新周期中这样做。 如果下载的内容用户没有阅读(使用)则这样做会浪费大量的流量以及电量,所以当你要实现该功能的时候应该小心谨慎(比如 Google Reader在设置中就有个选项是否定时抓取新的文章供离线阅读,其实现方式就是这样的)。

另外一种方式就是当用户连接到Wi-Fi的时候就一次性的下载所有数据,为了节约电量还可以附件一个条件:仅当充电的时候才下载。在
基于连接类型来修改下载模式 中我们再来研究这种情况。

批量传输和通信

每当您启动一个连接去下载数据– 不管数据大小如何 — 当使用3G信号的时候都会导致将近消耗20秒的电量。

如果一个程序每隔20秒去ping一下服务器,仅仅为了告诉服务器这个客户端还在线并且正在使用过程中,虽然您没有发送数据但是同样会导致设备的无线信号无法进入空闲状态,而大量的消耗设备电量。

所以要时刻记得捆绑您的数据传输并且创建一个延时的传输队列。如果做法恰当,则可以在同一时间窗口传输所有的数据,这样就可以尽可能减少无线信号的活动时间。
With that in mind it’s important to bundle your data transfers and create a pending transfer queue. Done correctly, you can effectively phase-shift transfers that are due to occur within a similar time window, to make them all happen simultaneously—ensuring that the radio draws power for as short a duration as possible.

这种方式的基本理念就是在每次传输数据的时候就尽可能多的传输所有您需要的数据。
The underlying philosophy of this approach is to transfer as much data as possible during each transfer session in an effort to limit the number of sessions you require.

这就意味着您需要把延时队列中的数据和定期的更新数据以及预抓取数据同时传输。类似的,您的定期更新和周期性的预抓取应该安排在同一个队列中同时请求数据。

让我们回到前面 预读取数据一节中的示例来看看具体是如何做的。

还是拿这个新闻阅读程序来举例,该程序使用上述的预读取技术。同时该程序还会收集用户的阅读习惯等信息来分析那些新闻对用户是比较重要的、感兴趣的,这样可以按照重要程度来排序这些新闻。为了及时更新内容程序会每隔1小时更新一次。为了节约流量,该程序每次只预读取缩略图当用户阅读新闻报道的时候才下载里面的图片。

上面的示例中,程序所收集的信息应该和更新数据一起传输,而不是收集一点数据就传输一点。可以在用户阅读新闻时下载新闻里面图片的时候一起传输这些分析数据;也可以在每小时更新数据的时候传输这些分析数据。

任何时间敏感或者即刻需要的数据 — 例如 当用户阅读新闻的时候下载新闻内的图片 — 应该比常规更新数据的优先级要高。常规更新可以在这些数据传输完成后继续传输,然后重新设置后续的常规更新间隔。这样就可以充分利用下载即可需要数据所建立的连接并延长后续的常规更新,从而可以最小化电量的消耗。

建设连接的使用次数

一般来说重用现有的数据连接比重新启动一个会更加高效。重用连接可以让网络更加智能的处理数据拥堵和传输网络数据的问题。

和同时创建多个连接去下载数据的情况相比,您可以选择用一个连接去连续的发送多个GET请求, 如果有可能的话最好选择把这些GET请求打包的一起一次发送。

另外,当连接空闲一段数据后,无线信号会被激活去发送一个客户端和服务器端断开连接的数据包,所以当你不再使用数据连接的时候及时关闭数据连接是一个比较好的做法,而不是等待其自动超时。

也就是说:太早的关闭连接会阻止其被重用,然后会需要附加的时间重新建立一个连接。这和尽量重用数据连接是有矛盾的,所以经常用一种妥协的方式:并不立即关闭数据连接而是等待一段时间(该时间一般比自动超时的时间要短)不用后再关闭。

使用DDMS Network Traffic 工具来分析疑难杂症

Android DDMS (Dalvik Debug Monitor Server) 包含一个网络使用情况的统计功能,可以追踪您的程序何时何地使用了网络。据此分析可以用来优化您的代码。

图三显示的是一个每隔15秒传输少量数据的例子,通过预抓取或者捆绑数据传输可以显著的降低对电量的消耗。

图三 使用DDMS来追踪网络使用情况

通过检测数据传输的频度和每次传输的数据量大小,可以分析出可以改进的地方。简单的来说,就是寻找那些可以延时传输或者捆绑传输突起的小刺。

为了更好的甄别这些小刺,Traffic Stats API 可以通过 TrafficStats.setThreadStatsTag() 函数来设置某个线程的tag(标签),然后通过 tagSocket()untagSocket()来添加标签或者取消标签。例如:

Apache HttpClientURLConnection 库会根据 getThreadStatsTag() 值自动的给每个Socket添加标签。 These libraries also tag and untag sockets when recycled through keep-alive pools.

Socket tagging is supported in Android 4.0, but real-time stats will only be displayed on devices running Android 4.0.3 or higher.

本文出自 云在千峰,转载时请注明出处及相应链接。

本文永久链接: http://blog.chengyunfeng.com/?p=381

Ɣ回顶部