本文譯自: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_REALTIME或RTC類型,而不是等同於_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);
}
}
另外,要容忍傳輸的失敗(如定期的更新),這樣就可以簡單的忽略失敗的連接和傳輸的嘗試。