Android官方文章翻译之管理设备苏醒状态(Managing Device Awake State)(二)
這是Managing Device Awake State的下半篇,上半篇請看:Android官方文章翻譯之管理設備蘇醒狀態(Managing Device Awake State)(一)
在了解接下來的內容之前,有必要先看一下這篇來自?FireOfStar的專欄?的文章:AndroidManifest.xml文件詳解(receiver)
?
2.執行重復報警(Scheduling Repeating Alarms)
Alarms(報警,報警器,警報,鬧鐘!下面的描述中會有用到這三個中文解釋之一或者英文Alarm)(基于 AlarmManager?類)為你提供了一種在你的程序的生命周期之外執行基于時間的操作的方式,例如,你可以使用一個Alarm來啟動一個長時間運行的操作,比如每天啟動一個服務去下載天氣預報信息。
Alarms具備這些特性:
1.使得你可以設定的時間或者一段時間間隔激活/觸發一個Intent意圖。
2.你可以使用它們聯合一個廣播接收器去啟動服務并執行其他一些列的操作。
3.它們不在你的應用程序生命周期范疇內,因此你可以使用它們觸發事件或者行為甚至當你的程序還未運行時,更甚是你的設備處于休眠狀態中時。
4.它們幫助你最大限度減少你的程序的資源請求,你可以不依賴時間或者一直在后臺運行服務執行操作。
注意:為了確保基于時間的操作在程序的生命周期期間得到執行,可以考慮使用?Handler?類結合?Timer?類和?Thread?類替代。這種方法可以讓安卓系統更好的控制系統資源。
?
明白權衡(Understand the Trade-offs)
一個重復的報警是一個相對簡單具有有限的靈活性的機制,它可能不是你的程序的最佳選擇,特別是當你需要的是激發網絡請求時。一個糟糕/不良的報警設計會引起電池的消耗,放入一個沉重的加載任務到服務中。
一個在你的程序生命周期之外激發一個請求的通用場景是使用一個服務來同步數據,這是你可能嘗試使用一個重復報警的一種情況。但是如果你的這個服務持有著你的程序數據,使用??Google Cloud Messaging?(GCM) 結合??sync adapter(翻譯成同步適配器!?)?會是比使用 AlarmManager 的一個更好的解決方案。一個同步適配器會為你提供與 AlarmManager 一樣的執行選項,但是它會顯著的更加靈活。例如,一個同步可以基于來自服務或設備的"新數據"消息(查看 ?Running a Sync Adapter?了解更多詳情),用戶的活動(或者空閑),一天內的時間等等。請查看頁面頂部的鏈接視頻來查看關于如何使用GCM和同步適配器的詳細的討論。
?
最佳實踐(Best practices)
你在設計你的重復報警時做出的每一個選擇都會對你的程序如何使用(或者說濫用)系統資源產生影響。例如,假設一個流行的程序同步服務器數據,如果一個同步操作是基于鬧鐘時間,所有的程序實例都在上午11點時同步,在服務器上的負載可能會導致高延遲甚至是“拒絕服務”。下面是使用Alarms的最佳實踐:
1.在一個以任何的網絡請求作為激發結果的重復報警中加入隨機性(抖動的):
1.1 當報警觸發時做一些本地的工作,“本地工作”意思是不與服務器連接或者從服務器請求數據的任何事情。
1.2 在同一時間,在同一段時間內的隨機時間激活包含網絡請求的報警。
2.保持你的報警的頻率最小化。
3.非必要時不要喚醒你的設備(喚醒或不喚醒設備的行為由Alarm的類型決定,在下面的內容(?Choose an alarm type)中會討論到)。
4.不要讓你的報警器的激發時間比它本應設定的時間更加精確。(即設定盡量模糊的時間,下面會有說明。)
使用?setInexactRepeating()?方法來替代?setRepeating()?方法。當你使用?setInexactRepeating()?時,Android會在同一個時間同步的執行來自多個程序的重復報警任務。這樣可以減少系統必須喚醒設備的總次數,由此減少對電池的消耗。從Android4.4(API版本為19)開始,所有的重復報警器都是inexact的(非精確模式)。注意,雖然?setInexactRepeating()?是對?setRepeating()?的一種改進,如果所有的程序實例都在幾乎同一時間激活服務,這仍然會壓倒服務。因此,對于網絡請求,應該添加隨機性到你的報警器中,就像上面討論過的。
5.盡可能的避免你的報警器基于系統鬧鐘時間。
一個基于精確的激活時間的重復報警器沒有很好的伸縮性,如果可以,使用?ELAPSED_REALTIME。不同的報警器類型在下面的內容中將由更加詳細的描述。
?
設置一個重復報警器(Set a Repeating Alarm)
正如上面所描述的,重復報警器是用來執行具有規律的事件或者數據取回操作的良好選擇。一個重復報警器具備如下特性:
1.一個報警器類型,更多詳細信息,請看下面的“選擇一個報警器類型”(Choose an alarm type)
2.一個觸發時間。如果你所指定的時間是過去的,那么報警器將被立即觸發。
3.報警器的間隔時間。例如,一天一次,每個小時一次,每五分鐘一次等等。
4.一個當報警器觸發時的待處理意圖。當你用這個相同的待處理意圖設置了第二個報警器,它將替換掉原始的報警器。
?
選擇一個報警器類型(Choose an alarm type)
在使用一個重復報警器時第一個要考慮的就是它應該配置哪種類型。
報警器有兩種通用的鬧鐘類型:“elapsed real time(過去的真實時間)”(官方沒有給出簡寫,為了方便,我這里描述成ERT)和“real time clock(基于鬧鐘的真實時間)”(RTC),ERT引用“從系統啟動之后經過的時間”,RTC引用UTC時間(即掛鐘),這意味著ERT適合用在設置基于時間的推移的報警器(例如,一個每隔30分鐘激活一次的報警器)因為它不會受時區/區域影響。RTC更適合依賴于當前區域的報警器。
這兩個類型都有一個“wakeup(喚醒)”版本,表示當屏幕處于關閉狀態時會喚醒設備的CPU。這能保證報警器在指定的執行時間被激活。如果你的程序有時間依賴性,這是非常有用的,例如,如果它有一個有限的窗口中執行特定的操作。如果你不使用喚醒版本的報警器類型,那么當你的設備蘇醒時,所有的重復報警器將被激活。
如果你只是簡單的需要你的報警器在一個特定的時間間隔時激活(例如,每半個小時),那么請使用ERT類型中的一種。通常,這是最好的選擇。
如果你需要想要在一天的特定時間激活你的報警器,那么選擇基于時鐘的RTC類型中的一種。注意,然而,這種方法會有一些缺點--程序可能無法很好的完成與其他區域的時間轉換,并且如果用戶修改了設備的時間設置,這將會使你的程序引起非預期的行為。使用RTC報警器類型同樣不具備很好的伸縮性,正如上面描述的。我們建議如果可能盡量使用ERT類型的報警器。
下面是類型列表:
1.ELAPSED_REALTIME -- 基于從設備啟動開始的一定時間激活待處理意圖,但是不會喚醒設備。這種類型的時間會把設備處于休眠狀態期間經過的時間一并計算在內。
2.ELAPSED_REALTIME_WAKEUP -- 與 的區別就是它會喚醒設備。
3.RTC -- 在特定的時間激活待處理意圖,但是不喚醒設備。
4.RTC_WAKEUP -- 與 RTC 的區別就是它會喚醒設備。
?
ELAPSED_REALTIME_WAKEUP舉例
這里有一些使用?ELAPSED_REALTIME_WAKEUP?的示例。
1.30分鐘之后喚醒設備激活報警器,在這之后每隔30分鐘再次激活:
// Hopefully your alarm will have a lower frequency than this! alarmMgr.setInexactRepeating(AlarmManager.ELAPSED_REALTIME_WAKEUP,AlarmManager.INTERVAL_HALF_HOUR,AlarmManager.INTERVAL_HALF_HOUR, alarmIntent);2.1分鐘之后喚醒設備激活一次報警器(不重復):
private AlarmManager alarmMgr; private PendingIntent alarmIntent; ... alarmMgr = (AlarmManager)context.getSystemService(Context.ALARM_SERVICE); Intent intent = new Intent(context, AlarmReceiver.class); alarmIntent = PendingIntent.getBroadcast(context, 0, intent, 0);alarmMgr.set(AlarmManager.ELAPSED_REALTIME_WAKEUP,SystemClock.elapsedRealtime() +60 * 1000, alarmIntent);?RTC舉例
這里有一些使用 RTC_WAKEUP 的示例。
1.在大約下午2點時喚醒設備激活報警器,此后每天在相同的時間點重復一次。
// Set the alarm to start at approximately 2:00 p.m. Calendar calendar = Calendar.getInstance(); calendar.setTimeInMillis(System.currentTimeMillis()); calendar.set(Calendar.HOUR_OF_DAY, 14);// With setInexactRepeating(), you have to use one of the AlarmManager interval // constants--in this case, AlarmManager.INTERVAL_DAY. alarmMgr.setInexactRepeating(AlarmManager.RTC_WAKEUP, calendar.getTimeInMillis(),AlarmManager.INTERVAL_DAY, alarmIntent);?2.在精確的時間點,早上8點30分時喚醒設備激活報警器,此后每隔20分鐘激活。
private AlarmManager alarmMgr; private PendingIntent alarmIntent; ... alarmMgr = (AlarmManager)context.getSystemService(Context.ALARM_SERVICE); Intent intent = new Intent(context, AlarmReceiver.class); alarmIntent = PendingIntent.getBroadcast(context, 0, intent, 0);// Set the alarm to start at 8:30 a.m. Calendar calendar = Calendar.getInstance(); calendar.setTimeInMillis(System.currentTimeMillis()); calendar.set(Calendar.HOUR_OF_DAY, 8); calendar.set(Calendar.MINUTE, 30);// setRepeating() lets you specify a precise custom interval--in this case, // 20 minutes. alarmMgr.setRepeating(AlarmManager.RTC_WAKEUP, calendar.getTimeInMillis(),1000 * 60 * 20, alarmIntent);決定你的報警器應該有多精確(Decide how precise your alarm needs to be)
正如上面所描述的,在創建一個報警器的第一步,經常是選擇一個報警器類型,進一步區分下來就是決定你的報警器應該有多精確。對于大多數程序,setInexactRepeating() 是正確的選擇。當你使用這個方法,Android會同步多個非精確模式下的重復報警器然后在同一時間激發它們。這將減少對電池的消耗。
部分少見的程序對時間有嚴格的要求--例如,報警器需要在精確的時間,早上8點30分激發,以及此后的每一個小時激活--使用 setRepeating(),但是如果可能應該盡量避免使用這種精確的時間模式。
使用 setInexactRepeating(),你無法像使用 setRepeating() 那樣指定一個自定義的間隔時間,你必須使用這些時間間隔變量中的一個,如 INTERVAL_FIFTEEN_MINUTES, INTERVAL_DAY 等等。請到 AlarmManager 中查看完整的清單。
?
取消報警器(Cancel an Alarm)
依據你的程序,你或許想要具備取消報警器的功能,為了取消一個報警器,在AlarmManager中調用 cancel() 方法。傳入你不再想要激發的 PendingIntent 對象。例如:
// If the alarm has been set, cancel it. if (alarmMgr!= null) {alarmMgr.cancel(alarmIntent); }當設備啟動時啟動報警器(Start an Alarm When the Device Boots)
默認情況下,所有的報警器在設備被關閉時都被取消。為了阻止這種情況發生,你可以設計使得你的程序可以在用戶重啟設備時自動的重啟你的重復報警器。這確保了 AlarmManager 能夠繼續執行它的工作而無需用戶手動的重啟它們。
下面是步驟:
1.在你的程序的manifest清單中設置 RECEIVE_BOOT_COMPLETED 權限。這將允許你的程序接收 ACTION_BOOT_COMPLETED 廣播,當設備完成啟動時,會發出該廣播。(前提是在本次開機之前,之前至少有一次用戶啟動過本程序)。
<uses-permission android:name="android.permission.RECEIVE_BOOT_COMPLETED"/>2.實現一個 類接收廣播。
public class SampleBootReceiver extends BroadcastReceiver {??? ? @Override?? ? public void onReceive(Context context, Intent intent) {?? ? ? ? if (intent.getAction().equals("android.intent.action.BOOT_COMPLETED")) {?? ? ? ? ? ? // Set the alarm here.?? ? ? ? }?? ? }?}?
3.向你的manifest清單中注冊該廣播,并使用 ACTION_BOOT_COMPLETED 類型的過濾類型來添加一個過濾器。
<receiver android:name=".SampleBootReceiver"??android:enabled="false">?? ?<intent-filter>?? ? ? ?
<action android:name="android.intent.action.BOOT_COMPLETED"></action>?? ?
</intent-filter>?
</receiver>
?
?
注意這個manifest文件,這個監聽設備啟動的接收器被設置成了 android:enabled="false" ,這意味著該接收器不會被調用,除非程序顯示的啟用它。這阻止了該廣播的非必要的調用。你可以按照如下的方法來啟用一個廣播接收器(例如,如果用戶設置了報警器來激活它):
ComponentName receiver = new ComponentName(context, SampleBootReceiver.class); PackageManager pm = context.getPackageManager();pm.setComponentEnabledSetting(receiver,PackageManager.COMPONENT_ENABLED_STATE_ENABLED,PackageManager.DONT_KILL_APP);?
?
一旦你通過這種方法啟用接收器,它將一直保持啟動狀態,甚至是用戶了重啟設備時。換句話說,以編程的方式啟用接收器會覆蓋在manifest中的設置,即便是重啟后。這個接收器將一直保持著開啟狀態直到你的程序禁用它,你可以按照如果的放啊來禁用一個廣播接收器(例如,如果用戶取消了報警器):
ComponentName receiver = new ComponentName(context, SampleBootReceiver.class); PackageManager pm = context.getPackageManager();pm.setComponentEnabledSetting(receiver,PackageManager.COMPONENT_ENABLED_STATE_DISABLED,PackageManager.DONT_KILL_APP);?
?
完成...
轉載于:https://www.cnblogs.com/emmet7life/p/4812480.html
總結
以上是生活随笔為你收集整理的Android官方文章翻译之管理设备苏醒状态(Managing Device Awake State)(二)的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: UVALive 4254 Process
- 下一篇: 理解Java的封装与接口