Android---最大限度的减少定期更新对电池的影响



本文译自:http://developer.android.com/training/efficient-downloads/regular_updates.html

最佳的定期更新频率经常会基于设备的状态、网络的联通性、用户的行为,以及明确的用户设置。

在“优化电量消耗”的系列文章中,讨论了如何基于主机设备的状态来修改刷新频率,从而构建高效使用电池的应用程序。其中包括在丢失联通性时禁止后台服务更新,以及在低电量的时候减少更新频率等。

本文讨论如何基于底层的无线信号状态机来改变刷新频率,并最大限度的减少对后台更新的影响。

使用Google的云消息服务来替换轮询

当应用程序采用轮询机制来检查服务端是否有更新时,都要激活无线信号,这样如果是基于3G的连接,那么都会有20秒以上的不必要的电量消耗。

Google针对Android的云消息服务(GCM)是一种用于从服务端向特定应用实例传输数据的轻量级机制。使用GCM,服务端可以通知运行在特定设备上的应用程序,有新的可用的数据给它。

相比应用程序定期向服务器查询新数据的轮询机制,使用GCM的这种事件驱动模式的应用程序,只会在有新数据下载时才创建一个新的连接。

这种结果不但会减少不必要的连接,而且也会减少应用程序内更新数据的延迟。

实现GCM要使用持久化的TCP/IP连接。当然实现自己的推送服务也是可能的,但最好使用GCM。这回最大限度的减少持久化连接的数量,并允许平台来优化带宽和减少对电池寿命的影响。

用不定时的重复提醒和指数退避的方式来优化轮询

在需要使用轮询的地方,一种好的做法是设置默认的数据刷新频率,并尽可能的降低对用户体验的影响。

一种简单的方法是提供设置选项,让用户来明确的设置所需要的更新频率,从而允许用户自己来定义数据刷新和电量消耗之间的平衡。

在使用不定时重复提醒来规划更新时,允许系统瞬移每个提醒所触发的准确时刻。

int alarmType=AlarmManager.ELAPSED_REALTIME;
long interval = AlarmManager.INTERVAL_HOUR;
long start = System.currentTimeMillis() + interval;

alarmManager.setInexactRepeating(alarmType, start, interval, pi);

如果在相似的时刻规划了几个提醒要触发,这种瞬移会导致它们被同时触发,这样就允许每个更新都基于一个活跃的无线信号状态变化之上来执行更新。

只要有可能,就要把提醒设置为ELAPSED_REALTIMERTC类型,而不是等同于_WAKEUP的类型。这样就能够更加有效的减少对电池的影响,因为只有在电话处于非待机模式中才会触发提醒。

还可以基于最近应用程序的使用情况,通过减少相应频率的方法来进一步的减少这些计划提醒的影响。

一种方法是实现一种指数退避方案,以便在应用程序不曾使用之前的更新来降低更新频率。这有利于维护一个最小的更新频率,并且无论应用程序什么时候使用之前更新过的数据,都会重新设置频率,例如:

SharedPreferences sp =
  context.getSharedPreferences(PREFS, Context.MODE_WORLD_READABLE);

boolean appUsed = sp.getBoolean(PREFS_APPUSED, false);
long updateInterval = sp.getLong(PREFS_INTERVAL, DEFAULT_REFRESH_INTERVAL);

if (!appUsed)
  if ((updateInterval *= 2) >MAX_REFRESH_INTERVAL)  
    updateInterval = MAX_REFRESH_INTERVAL;

Editor spEdit = sp.edit();
spEdit.putBoolean(PREFS_APPUSED, false);
spEdit.putLong(PREFS_INTERVAL, updateInterval);
spEdit.apply();

rescheduleUpdates(updateInterval);
executeUpdateOrPrefetch();

还可以使用与指数退避方案类似的方法来减少连接失败和下载错误的影响。

不管能否连接到服务器或是否下载到数据,发起网络连接所花费的成本是一样的。对于时间敏感的传输,其成功完成是十分重要的,因此为最大限度的减少对电池的影响,要使用能够减少重试频率的指数退避算法,例如:

privatevoid retryIn(long interval){
  booleansuccess = attemptTransfer();
   
  if (!success) {
    retryIn(interval*2 <MAX_RETRY_INTERVAL ?
            interval*2 : MAX_RETRY_INTERVAL);      
  }
}

另外,要容忍传输的失败(如定期的更新),这样就可以简单的忽略失败的连接和传输的尝试。

你可能感兴趣的:(Android---最大限度的减少定期更新对电池的影响)