声明:翻译自google官方SDK文档!适用于android2.0(API level 5)以及更高版本。
最大限度地减少定期更新的影响
定期更新的最佳频率将根据设备状态,网络连接,用户行为,以及明确的用户的喜好而有所不同。
Optimizing Battery Life 讨论了如何构建一个根据主机设备的状态来修改更新频率的节电程序。包括当失去连接时禁用后台服务更新和电池电量低时降低更新频率。
本课程将讲述如何根据底层无线射频的状态来更改你的后台更新来达到最好的效果 。
1.使用Google云端推送来替代轮询
在3G网络环境下,应用程序每次轮询服务器检查是否需要更新都会激活无线射频20秒,造成不必要功率消耗。
Google云端推送(GCM)是一从服务器传输数据到特定应用程序的轻量级的机制。利用GCM,你的服务器能够通知你的运行在指定设备上的程序有新数据可用。
相较于轮询机制,在轮询机制下,你的程序必须定期的ping服务器来查询是否有新的数据,这种机制下你的程序必须创建一个新的连接来ping服务器,才能够知道是否有新数据。
(使用GCM)其结果是不必要的连接减少,并为您的应用程序中更新数据的减少延迟。
GCM使用持续的TCP/IP连接实现,虽然TCP/IP可以自己实现推送,但是最好的做法是使用GCM。使用GCM能够最大限度地减少持久连接的数量,并允许以优化带宽并减少对电池寿命的影响相关联的平台。
2.使用 Inexact Repeating Alarms 和指数回退来优化轮询
如果轮询是必需的,那么应该在尽可能不降低用户体验的前提下尽量降低默认更新的频率。
一个简单的方法是,允许用户在界面上设置更新的频率,使用户可以自己定义数据的更新和电量的消耗之间的平衡。
当调度更新时,应该使用 AlarmManager实现,每当AlarmManager发出警报,系统就能够接收到来触发事件。
int alarmType = AlarmManager.ELAPSED_REALTIME; long interval = AlarmManager.INTERVAL_HOUR; long start = System.currentTimeMillis() + interval; alarmManager.setInexactRepeating(alarmType, start, interval, pi);具体关于 AlarmManager介绍见:http://www.360doc.com/content/13/0110/23/11991_259460170.shtml#
如果有多个 alarms 预计同时触发,那么它们几乎会同时触发。每个更新都会带来一个单独的无线射频的状态改变。
尽可能的设置 alarm的类型 type为 ELAPSED_REALTIME(在系统休眠状态下不可用,使用相对系统时间)或者RTC(在系统休眠状态下不可用,使用绝对系统时间)而不要使用_WAKEUP(会在系统休眠状态下也可用),这样就不会在系统休眠状态下触发 alarm,来降低电量的消耗。
也应该根据app是否使用最近更新的数据来降低alarms的触发。如果最近更新的数据没有被app使用,那么要进行类似的指数回退模式来降低更新频率,如下:
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();发起网络连接的成本是一样的,不管你是能够联系您的服务器和下载数据或没有。对于对时间敏感的传输,其中在成功完成是很重要的,一个指数回退算法,可用于降低重试的次数,以减少相关联的电池的影响,例如:
private void retryIn(long interval) { boolean success = attemptTransfer(); if (!success) { retryIn(interval*2 < MAX_RETRY_INTERVAL ? interval*2 : MAX_RETRY_INTERVAL); } }另外如果对于失败连接不是敏感的话(例如定期更新)可以简单的忽略失败连接的尝试。