《Android优化专题》—— 优化下载效率

一、用有效率的网络访问方式来优化下载

1.无线电状态机

  • Full power:当无线连接被激活的时候,允许设备以最大的传输速率进行操作。
  • Low power:相对Full power来说,算是一种中间状态,差不多50%的传输速率。
  • Standby:最低的状态,没有数据连接需要传输。

典型的3G无线电波状态机图示:

image

2. apps如何影响无线状态机

创建新连接

每次创建一个网络连接,无线电波就会切换到full power状态(这也是为什么降低连接数可以更省电量以及省流量的原因),结束之后会有一个附加的5s时间切换到low power,再之后会经过12s进入到low energy状态,每次数据传输的会话都会引起无线电波持续消耗大概20s的能量。

传输bundle(序列化)与unbundled(未序列化)数据差别

image

一个app传递1秒钟的unbundled data会使得无线电波持续活动18秒【18=1秒的传输数据+5秒过渡时间回到low power+12秒过渡时间回到standby】。因此每一分钟,它会消耗18秒high power的电量,42秒的low power的电量。

如果每分钟app会传输bundle的data持续3秒的话,其中会使得无线电波持续在high power状态仅仅8秒钟,在low power状态仅仅12秒钟。 上面第二种传输bundle data的例子,可以看到减少了大量的电量消耗。

3. 预取数据

预取数据是一种减少独立数据传输会话数量的有效方法。在单次操作的时候,通过一次连接,在最大能力下,根据给出的时间下载到所有的数据。

对于预取,取太多,不仅用户可能根本用不到那么多,而且还耗费了电量和流量。取太少,达不到预取的效果。

如何控制预取的大小?

这需要根据正在下载的数据大小与可能被用到的数据量来决定。一个基于上面状态机情况的比较大概的建议是:对于数据来说,大概有50%的机会可能用在当前用户的会话中,那么我们可以预取大约6秒(大约1-2Mb),这大概使得潜在可能要用的数据量与可能已经下载好的数据量相一致。

通常来说,预取1-5Mb会比较好,这种情况下,我们仅仅只需要每隔2-5分钟开始另一段下载。根据这个原理,大数据的下载,比如视频文件,应该每隔2-5秒开始另一段下载,这样能有效的预取到下面几分钟内的数据进行预览。

4. 批量传输与连接

每次初始化一个连接(与需要传输的数据量无关),有可能导致无线电波持续花费20s的电量。

对于数据进行bundle操作,并且创建一个序列可以使得大量数据集中进行发送,这样可以使得无线电波的激活时间尽可能的少,同事减少大部分的电量花费。

5. 减少连接次数

重用之前存在的网络连接比重新创建一个连接是更有效率的。当可以用一个GET请求解决的情况下,不要同时创建多个网络连接。

可以在一个连接要关闭时,不要立即关闭,而是在timeout之前关闭。

使用HttpUrlConnection,而不是HttpClient,前者做了response cache

6. 使用DDMS(Dalvik Debug Monitor Server)网络通信工具来检测网络使用情况

image

通过监测数据传输的频率和每次传输的数据量,可以看出哪些地方可以进行优化。类似于图中短小钉子形状的地方,可以和附近位置的请求进行merge操作。

Traffic Status API可以使用TrafficStats.setThreadStatsTag()的方法标记数据传输发生在某个Thread里面。可以手动使用tagSocket()进行标记或者untagSocket()来取消标记。

二、调整更新的频率

1.使用C2DM作为轮询方式之一

C2DM是一个用来从server到特定app传输数据的轻量级机制。使用C2DM,server会在某个app有需要获取新数据的时候通知app有这个消息。

但中国大陆的Google框架通常会被移除,使得C2DM没法在中国大陆的App上使用。

可参考各大厂商的推送定制。

2.通过不定时的重复提醒和指数退避来优化轮询操作

如果必须要使用轮询机制,可以考虑以下几个方面的优化:

  1. 如果多个提醒都安排在某个小的时间段内,考虑把这多个操作在一个无线电状态下操作完。
  2. 使用Alarm时,设置的提醒类型为非wake类型(对于非紧急通知消息时,避免在屏幕熄灭状态下,将设备唤醒),减少电量的损耗。
  3. 在app上一次更新操作之后还未被使用的情况下,使用指数退避算法(exponential back-off algorithm)来减少更新频率。
private void retryIn(long interval) {
  boolean success = attemptTransfer();

  if (!success) {
    retryIn(interval*2 < MAX_RETRY_INTERVAL ?
            interval*2 : MAX_RETRY_INTERVAL);
  }
}

二进制退避算法

  1. 确定基本退避时间,一般为端到端的往返时间为2t,2t也成为冲突窗口或争用期。
  2. 定义参数k,k与冲突次数有关,规定k不能超过10,k=Min[冲突次数,10]。在冲突次数大于10,小于16时,k不再增大,一直取值为10。
  3. 从离散的整数集合[0,1,2,……,(2k-1)]中随机的取出一个数r,等待的时延为r倍的基本退避时间,等于r x 2t。r的取值范围与冲突次数k有关,r可选的随机取值为2k个、这也是称为二进制退避算法的起因。
  4. 当冲突次数大于10以后,都是从0—210-1个2t中随机选择一个作为等待时间。
  5. 当冲突次数超过16次后,发送失败,丢弃传输的帧,发送错误报告。

三、使用缓存来避免重复下载

减少下载的最基本方法是仅仅下载你想要的数据,通过类似上次更新时间来制定查询数据的条件。在下载图片时,server尽量减少图片的大小,比如对图片进行剪裁等处理。

1. 缓存到本地

long currentTime = System.currentTimeMillis());

HttpURLConnection conn = (HttpURLConnection) url.openConnection();

long expires = conn.getHeaderFieldDate("Expires", currentTime);
long lastModified = conn.getHeaderFieldDate("Last-Modified", currentTime);

setDataExpirationDate(expires);

if (lastModified < lastUpdateTime) {
  // Skip update  
} else {
  // Parse update  
}

可以使用下面的方法获取External缓存目录:目录是Android/data/data/com.xxx.xxx/cache

Context.getExternalCacheDir();

获取内部缓存的方法

Context.getCache()

这里注意不要随便在sdcard下创建目录存放缓存,因为这个文件夹不会随着程序的卸载而删除。既影响用户体验,又会把一些不想让用户知道的数据泄露出去。

2. 使用HttpUrlConnect Response缓存

我们可以通过反射机制开启HTTP response cache

private void enableHttpResponseCache() {
  try {
    long httpCacheSize = 10 * 1024 * 1024; // 10 MiB  
    File httpCacheDir = new File(getCacheDir(), "http");
    Class.forName("android.net.http.HttpResponseCache")
         .getMethod("install", File.class, long.class)
         .invoke(null, httpCacheDir, httpCacheSize);
  } catch (Exception httpResponseCacheNotAvailable) {
    Log.d(TAG, "HTTP response cache is unavailable.");
  }
}

也可以在onCreate中加入以下代码开启

  protected void onCreate(Bundle savedInstanceState) {
       ...

       try {
           File httpCacheDir = new File(context.getCacheDir(), "http");
           long httpCacheSize = 10 * 1024 * 1024; // 10 MiB
           HttpResponseCache.install(httpCacheDir, httpCacheSize);
       } catch (IOException e) {
           Log.i(TAG, "HTTP response cache installation failed:" + e);
       }
   }

   protected void onStop() {
       ...

       HttpResponseCache cache = HttpResponseCache.getInstalled();
       if (cache != null) {
           cache.flush();
       }
   }

以上代码会在Android4.0以上开启response code,所有cache中的HTTP请求都可以直接在本地存储中响应,就不需要开启一个新的网络连接。被cache起来的response可以被server确保没有过期,这样减少了带宽。

四、根据网络类型来切换下载模式

WIFI要比无线电波消耗的电量要少很多,而且对于无线电波而言(3G,2G,LTE等)也存在不同电量的区别。

1. 尽量WIFI

我们尽量要在连接WIFI的时候进行下载,更新数据等操作。

2. 尽量使用更大的带宽下载更多的数据,而不是经常去下载

相对更宽的带宽会有更长的状态切换时间(从full power过渡到standby有更长一段时间的延迟),过渡时间的电量通常是固定的,每次传输会话过程中尽量一次性把事情做完,而不是断断续续请求就更有效率了。

如果LTE无线电的带宽与电量消耗都是3G无线电的2倍,我们应该在每次会话的时候都下载4倍于3G的数据量,或者是差不多10Mb

我们可以根据connectivity manager来判断当前激活的无线电波,根据结果来做prefetch。

ConnectivityManager cm =
 (ConnectivityManager)getSystemService(Context.CONNECTIVITY_SERVICE);

TelephonyManager tm =
  (TelephonyManager)getSystemService(Context.TELEPHONY_SERVICE);

NetworkInfo activeNetwork = cm.getActiveNetworkInfo();

int PrefetchCacheSize = DEFAULT_PREFETCH_CACHE;

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

推荐阅读更多精彩内容