AlarmManager
類別) 可讓您執行
位於應用程式生命週期之外的時間型作業。
舉例來說,您可以使用鬧鐘啟動長時間運行的作業,例如每天啟動一次服務來下載天氣預報。
鬧鐘具有下列特性:
可讓您在指定時間和/或間隔觸發意圖。
您可以將這兩者與廣播接收器搭配使用,排定工作或WorkRequest,以便執行其他作業。
這類函式會在應用程式之外運作,因此您可以透過這類函式 事件或動作 (即使裝置未執行) 睡著了
這有助於您盡量減少應用程式的資源需求。您可以安排作業,而不需要依賴計時器或持續執行的服務。
設定非精確鬧鐘
如果應用程式設定了非精確鬧鐘,系統就會在特定時間點觸發警報 不精確的鬧鐘可在遵守 Doze 等省電限制的情況下,提供鬧鐘傳送時間的部分保證。
開發人員可以運用下列 API 保證來自訂 非精確的鬧鐘放送方式
在指定時間後觸發鬧鐘
如果應用程式呼叫 set()
、setInexactRepeating()
或 setAndAllowWhileIdle()
,鬧鐘就不會在指定的觸發時間前響起。
在 Android 12 (API 級別 31) 以上版本中,系統會在其中叫用鬧鐘 的啟動時間 (除非已設定省電限制) 例如省電模式或 打盹。
在指定時間範圍內傳送鬧鐘
如果應用程式呼叫 setWindow()
,鬧鐘就不會響起
觸發時間。除非有省電限制,否則系統會在指定的時間範圍內,從指定的觸發時間開始發出鬧鐘。
如果應用程式指定 Android 12 以上版本,系統可以將時間窗口不精確鬧鐘的叫用延遲至少 10 分鐘。因此,600000
下方的 windowLengthMillis
參數值會裁剪為 600000
。
以大致固定的間隔發出重複鬧鐘
如果應用程式呼叫 setInexactRepeating()
,系統會叫用多個鬧鐘:
- 第一個鬧鐘會在指定時間範圍內響起 指定的觸發時間
- 後續的鬧鐘通常會在指定時間範圍後響起 如畫面所示兩次連續叫用鬧鐘的時間可能不同。
設定精確鬧鐘
系統會在未來的確切時間點叫用精確鬧鐘。
大多數應用程式都可使用非精確鬧鐘排定工作和活動時間,以完成幾個常見用途。如果應用程式的核心 功能取決於精確計時的鬧鐘,例如鬧鐘應用程式 或行事曆應用程式,則可設定確切的鬧鐘。
可能不需要精確鬧鐘的用途
以下列出不需要精確鬧鐘的常見工作流程:
- 在應用程式的生命週期中安排時間作業
Handler
類別包含數個優質 處理時間作業,例如每 n 秒時,在應用程式上線期間:postAtTime()
和postDelayed()
。 請注意,這些 API 會依賴系統正常運作時間,而非即時。- 排定的背景作業,例如:更新應用程式和上傳記錄檔
WorkManager
可讓您安排具時效性的定期週期 工作。 您可以提供重複間隔和flexInterval
(至少 15 分鐘),藉此定義精細的工作執行時間。- 在特定時間後所應採取的使用者指定動作 (即使系統處於閒置狀態)
- 請使用非精準鬧鐘。具體問題為
setAndAllowWhileIdle()
。 - 在特定時間後所應採取的使用者指定動作
- 請使用非精準鬧鐘。具體來說,請呼叫
set()
。 - 指定時限內可以進行的使用者指定動作
- 請使用非精準鬧鐘。具體來說,請呼叫
setWindow()
。請注意,如果應用程式指定 Android 12 以上版本,則允許的最小時間間隔為 10 分鐘。
設定精確鬧鐘的方式
應用程式可以使用下列其中一種方法設定精確鬧鐘。這些方法的排序方式是,越靠近清單底部的越是用於處理時間敏感度較高的任務,但需要更多系統資源。
setExact()
在未來某個時間點 (只要沒有其他省電措施) 叫用鬧鐘。
除非應用程式的工作對使用者而言是時間敏感的,否則請使用此方法設定精確鬧鐘。
setExactAndAllowWhileIdle()
在未來的某個時間點上,即使已啟用省電措施,也能準確觸發鬧鐘。
setAlarmClock()
在未來的特定時間點觸發鬧鐘。因為這些鬧鐘 對使用者的能見度很高,所以系統永遠不會調整運送時間。 系統會將這些警報視為最關鍵的警報,並留下低功耗 模式。
系統資源用量
系統觸發應用程式設定的精確鬧鐘時,裝置 會耗用大量資源,例如電池續航力 就會有節電模式此外,系統無法輕鬆批次處理這些要求 讓資源使用更有效率
我們強烈建議您建立不精確鬧鐘
如要執行較長的工作,請使用鬧鐘的 BroadcastReceiver
中的 WorkManager
或 JobScheduler
安排工作。如要在裝置處於 Doze 模式時執行工作,請使用 setAndAllowWhileIdle()
建立非精準鬧鐘,然後透過鬧鐘啟動工作。
宣告適當的精確鬧鐘權限
如果您的應用程式指定 Android 12 以上版本,就必須取得
鬧鐘與提醒"特殊的應用程式存取權如要這樣做,請在應用程式的資訊清單檔案中宣告 SCHEDULE_EXACT_ALARM
權限,如以下程式碼片段所示:
<manifest ...> <uses-permission android:name="android.permission.SCHEDULE_EXACT_ALARM"/> <application ...> ... </application> </manifest>
如果應用程式指定 Android 13 (API 級別 33) 以上版本,您可以選擇
宣告 SCHEDULE_EXACT_ALARM
或 USE_EXACT_ALARM
權限。
<manifest ...> <uses-permission android:name="android.permission.USE_EXACT_ALARM"/> <application ...> ... </application> </manifest>
SCHEDULE_EXACT_ALARM
和 USE_EXACT_ALARM
權限時
信號的能力相同、給予方式不同,也各能支援
用途只有在應用程式面向使用者的功能需要精確計時的操作時,應用程式才應使用精確鬧鐘,並聲明 SCHEDULE_EXACT_ALARM
或 USE_EXACT_ALARM
權限。
USE_EXACT_ALARM
- 已自動授予
- 使用者無法撤銷
- 須遵守即將發布的 Google Play 政策
- 少數用途
SCHEDULE_EXACT_ALARM
- 由使用者授予
- 更多用途
- 應用程式應確認權限未撤銷
未將 SCHEDULE_EXACT_ALARM
權限預先授予全新安裝項目
指定 Android 13 (API 級別 33) 以上版本的應用程式。如果使用者轉移應用程式
透過備份與還原作業將資料複製到搭載 Android 14 的裝置
在新裝置上將拒絕「SCHEDULE_EXACT_ALARM
」權限。不過,
現有應用程式已具備這項權限,系統會在
裝置升級至 Android 14。
注意:如果精確鬧鐘是透過 OnAlarmListener
物件設定,例如在 setExact
API 中,則不需要 SCHEDULE_EXACT_ALARM
權限。
使用 SCHEDULE_EXACT_ALARM
權限
與 USE_EXACT_ALARM
不同,SCHEDULE_EXACT_ALARM
權限必須由使用者授予。使用者和系統都能撤銷
SCHEDULE_EXACT_ALARM
權限。
如要確認應用程式是否已獲得權限,請先呼叫 canScheduleExactAlarms()
,再嘗試設定精確鬧鐘。當應用程式 SCHEDULE_EXACT_ALARM
權限遭到撤銷時,應用程式會停止運作,且日後所有精確鬧鐘都會取消。這也表示 canScheduleExactAlarms()
回傳的值在應用程式的整個生命週期內都會保持有效。
當 SCHEDULE_EXACT_ALARMS
權限授予應用程式時,系統會傳送 ACTION_SCHEDULE_EXACT_ALARM_PERMISSION_STATE_CHANGED
廣播。您的應用程式應實作廣播訊息
接收端
包括:
- 確認應用程式仍擁有特殊應用程式存取權。方法是呼叫
canScheduleExactAlarms()
。 如果使用者已授予您的應用程式 之後便會立即撤銷該權限。 - 根據應用程式目前狀態,重新排定應用程式需要的任何精確鬧鐘。這個邏輯應與應用程式收到
ACTION_BOOT_COMPLETED
廣播時的行為類似。
請使用者授予 SCHEDULE_EXACT_ALARM
權限
您可以視需要將使用者傳送到鬧鐘與系統中的提醒畫面 如圖 1 所示若要這樣做,請完成下列步驟:
- 在應用程式使用者介面中,向使用者說明應用程式需要排定確切時間的原因 鬧鐘。
- 請叫用含有
ACTION_REQUEST_SCHEDULE_EXACT_ALARM
意圖動作的意圖。
設定週期性鬧鐘
重複鬧鐘會讓系統定期通知應用程式 排程。
設計不良的鬧鐘可能會耗盡電池電量,並對伺服器造成大量負載。因此,在 Android 4.4 (API 級別 19) 以上版本中,所有重複鬧鐘都是不精確的鬧鐘。
重複鬧鐘具有下列特性:
鬧鐘類型。如需進一步討論,請參閱「選擇鬧鐘類型」。
觸發時間。如果您指定的觸發時間已過,鬧鐘會立即觸發。
鬧鐘的間隔。例如每天一次、每小時一次或每 5 分鐘一次。
鬧鐘觸發時會觸發的待處理意圖。設定 使用相同的待處理意圖的第二個鬧鐘則會取代原始鬧鐘。
如要取消 PendingIntent()
,請將 FLAG_NO_CREATE
傳遞至 PendingIntent.getService()
,以取得意圖的例項 (如果存在),然後將該意圖傳遞至 AlarmManager.cancel()
Kotlin
val alarmManager = context.getSystemService(Context.ALARM_SERVICE) as? AlarmManager val pendingIntent = PendingIntent.getService(context, requestId, intent, PendingIntent.FLAG_NO_CREATE) if (pendingIntent != null && alarmManager != null) { alarmManager.cancel(pendingIntent) }
Java
AlarmManager alarmManager = (AlarmManager) context.getSystemService(Context.ALARM_SERVICE); PendingIntent pendingIntent = PendingIntent.getService(context, requestId, intent, PendingIntent.FLAG_NO_CREATE); if (pendingIntent != null && alarmManager != null) { alarmManager.cancel(pendingIntent); }
選擇鬧鐘類型
使用重複鬧鐘時,首先要考慮的是鬧鐘的類型。
鬧鐘有兩種一般時鐘類型:「經過時間」和 「即時時鐘」(RTC)。 經過的實際時間會使用「系統啟動後的時間」做為參考,而實際時間時鐘會使用世界標準時間 (壁鐘時間)。這表示經過的實際時間適合用於設定根據時間間隔設定的鬧鐘 (例如每 30 秒觸發一次的鬧鐘),因為它不會受到時區或語言代碼的影響。即時時鐘類型最適合用於具有以下效果的鬧鐘 取決於目前的語言代碼。
這兩種類型都具有「喚醒」一詞版本,假定只要有需要,就要喚醒裝置的 CPU 螢幕也關掉了。確保鬧鐘會在排定的時間觸發。 如果應用程式有時間依附元件,這項功能就非常實用。例如,如果它有執行特定作業的有限時間。如果您未使用鬧鐘類型的喚醒版本,則裝置下次喚醒時,所有重複鬧鐘都會觸發。
如果您只需要鬧鐘在特定間隔 (例如每半小時) 觸發,請使用其中一種經過的實際時間類型。一般而言 是更好的選擇
如要讓鬧鐘在一天的特定時間觸發,請選擇鬧鐘 即時時鐘類型。不過,請注意,這種做法可能會有一些缺點。應用程式可能無法翻譯成其他語言代碼 使用者變更裝置的時間設定,可能會導致非預期的行為 。使用即時時鐘類型也不會縮放 。建議您使用「經過時間」鬧鐘 可以的話
類型清單如下:
ELAPSED_REALTIME
:根據裝置啟動後經過的時間,觸發待處理意圖,但不會喚醒裝置。 經過的時間包括裝置處於休眠狀態的任何時間。ELAPSED_REALTIME_WAKEUP
: 喚醒裝置,並在指定時間長度後觸發待處理意圖 所經過的時間。RTC
: 在指定時間啟動待處理意圖,但不會喚醒裝置。RTC_WAKEUP
:喚醒裝置,在指定時間觸發待處理的意圖。
已經過的即時鬧鐘範例
以下列舉幾個 ELAPSED_REALTIME_WAKEUP
的使用範例
每 30 分鐘喚醒裝置並觸發鬧鐘 之後:
Kotlin
// Hopefully your alarm will have a lower frequency than this! alarmMgr?.setInexactRepeating( AlarmManager.ELAPSED_REALTIME_WAKEUP, SystemClock.elapsedRealtime() + AlarmManager.INTERVAL_HALF_HOUR, AlarmManager.INTERVAL_HALF_HOUR, alarmIntent )
Java
// Hopefully your alarm will have a lower frequency than this! alarmMgr.setInexactRepeating(AlarmManager.ELAPSED_REALTIME_WAKEUP, SystemClock.elapsedRealtime() + AlarmManager.INTERVAL_HALF_HOUR, AlarmManager.INTERVAL_HALF_HOUR, alarmIntent);
喚醒裝置,在一分鐘內觸發單次 (非重複) 鬧鐘:
Kotlin
private var alarmMgr: AlarmManager? = null private lateinit var alarmIntent: PendingIntent ... alarmMgr = context.getSystemService(Context.ALARM_SERVICE) as AlarmManager alarmIntent = Intent(context, AlarmReceiver::class.java).let { intent -> PendingIntent.getBroadcast(context, 0, intent, 0) } alarmMgr?.set( AlarmManager.ELAPSED_REALTIME_WAKEUP, SystemClock.elapsedRealtime() + 60 * 1000, alarmIntent )
Java
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_WAKEUP
。
喚醒裝置,讓鬧鐘大約在下午 2 點響起,並每天在同一時間重複一次:
Kotlin
// Set the alarm to start at approximately 2:00 p.m. val calendar: Calendar = Calendar.getInstance().apply { timeInMillis = System.currentTimeMillis() 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.timeInMillis, AlarmManager.INTERVAL_DAY, alarmIntent )
Java
// 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);
喚醒裝置,讓鬧鐘在早上 8 點 30 分鐘準時響起,之後每 20 分鐘響一次:
Kotlin
private var alarmMgr: AlarmManager? = null private lateinit var alarmIntent: PendingIntent ... alarmMgr = context.getSystemService(Context.ALARM_SERVICE) as AlarmManager alarmIntent = Intent(context, AlarmReceiver::class.java).let { intent -> PendingIntent.getBroadcast(context, 0, intent, 0) } // Set the alarm to start at 8:30 a.m. val calendar: Calendar = Calendar.getInstance().apply { timeInMillis = System.currentTimeMillis() set(Calendar.HOUR_OF_DAY, 8) set(Calendar.MINUTE, 30) } // setRepeating() lets you specify a precise custom interval--in this case, // 20 minutes. alarmMgr?.setRepeating( AlarmManager.RTC_WAKEUP, calendar.timeInMillis, 1000 * 60 * 20, alarmIntent )
Java
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);
決定鬧鐘的精確程度
如先前所述,選擇鬧鐘類型通常是建立鬧鐘的第一步。另一個差異是鬧鐘的精確度。對於大多數應用程式來說,setInexactRepeating()
是正確的選擇。使用這個方法時,Android 會同步處理多個不精確的重複鬧鐘和火災。
相同的內容這麼做可以降低電池耗電量。
盡量避免使用精確鬧鐘。不過,對於少許嚴謹的應用程式
時間要求,可以撥打 [精確鬧鐘] 來設定
setRepeating()
。
透過 setInexactRepeating()
,
您無法像
setRepeating()
。
您必須使用其中一個間隔常數,例如
INTERVAL_FIFTEEN_MINUTES
、
INTERVAL_DAY
、
依此類推查看「AlarmManager
」
查看完整清單
取消鬧鐘
視應用程式而定,您可能想加入取消鬧鐘的功能。
如要取消鬧鐘,請在 Alarm Manager 上呼叫 cancel()
,並傳入您不再想觸發的 PendingIntent
。例如:
Kotlin
// If the alarm has been set, cancel it. alarmMgr?.cancel(alarmIntent)
Java
// If the alarm has been set, cancel it. if (alarmMgr!= null) { alarmMgr.cancel(alarmIntent); }
在裝置重新啟動時啟動鬧鐘
根據預設,系統會在裝置關閉時取消所有鬧鐘。
為避免這種情況發生,您可以設計應用程式,在使用者重新啟動裝置時自動重新啟動重複鬧鐘。這樣一來,AlarmManager
就能繼續執行其工作,而使用者不必手動重新啟動鬧鐘。
步驟如下:
在應用程式資訊清單中設定
RECEIVE_BOOT_COMPLETED
權限。這樣一來,應用程式就能接收ACTION_BOOT_COMPLETED
會在系統啟動完畢後播送 (只有在 應用程式已由使用者啟動至少一次):<uses-permission android:name="android.permission.RECEIVE_BOOT_COMPLETED"/>
實作
BroadcastReceiver
如何接收廣播訊息:Kotlin
class SampleBootReceiver : BroadcastReceiver() { override fun onReceive(context: Context, intent: Intent) { if (intent.action == "android.intent.action.BOOT_COMPLETED") { // Set the alarm here. } } }
Java
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. } } }
請使用意圖篩選器,將接收器新增至應用程式的資訊清單檔案,並篩選
ACTION_BOOT_COMPLETED
動作:<receiver android:name=".SampleBootReceiver" android:enabled="false"> <intent-filter> <action android:name="android.intent.action.BOOT_COMPLETED"></action> </intent-filter> </receiver>
請注意,在資訊清單中,啟動接收器設為
android:enabled="false"
。也就是說,除非應用程式明確啟用接收器,否則系統不會呼叫接收器。這麼做可避免不必要地呼叫開機接收器。您可以啟用接收器 (舉例來說,如果使用者設定鬧鐘),如下所示:Kotlin
val receiver = ComponentName(context, SampleBootReceiver::class.java) context.packageManager.setComponentEnabledSetting( receiver, PackageManager.COMPONENT_ENABLED_STATE_ENABLED, PackageManager.DONT_KILL_APP )
Java
ComponentName receiver = new ComponentName(context, SampleBootReceiver.class); PackageManager pm = context.getPackageManager(); pm.setComponentEnabledSetting(receiver, PackageManager.COMPONENT_ENABLED_STATE_ENABLED, PackageManager.DONT_KILL_APP);
以這種方式啟用接收器後,即使使用者重新啟動裝置,接收器仍會保持啟用狀態。換句話說,透過程式輔助方式啟用接收器會覆寫資訊清單設定,即使在重新啟動時也是如此。接收器會一直處於啟用狀態,直到應用程式停用為止。您可以停用接收器 (例如 如果使用者取消鬧鐘),請按照下列步驟操作:
Kotlin
val receiver = ComponentName(context, SampleBootReceiver::class.java) context.packageManager.setComponentEnabledSetting( receiver, PackageManager.COMPONENT_ENABLED_STATE_DISABLED, PackageManager.DONT_KILL_APP )
Java
ComponentName receiver = new ComponentName(context, SampleBootReceiver.class); PackageManager pm = context.getPackageManager(); pm.setComponentEnabledSetting(receiver, PackageManager.COMPONENT_ENABLED_STATE_DISABLED, PackageManager.DONT_KILL_APP);
在裝置處於休眠模式時叫用鬧鐘
搭載 Android 6.0 (API 級別 23) 的裝置支援 Doze 模式,可延長裝置電池續航力。裝置處於打盹模式時,鬧鐘不會觸發。任何預定的鬧鐘都會延遲,直到裝置退出打盹模式為止。如果您需要在裝置閒置時完成工作,可以使用下列幾種做法:
設定精確鬧鐘。
請使用 WorkManager API,這是專為執行背景工作而建構的 API。您也可以指定系統應加快作業速度 工作會盡快完成詳情請參閱「使用 WorkManager 安排工作」
最佳做法
設計週期性鬧鐘時所做的任何選擇都可能造成後果 瞭解應用程式使用 (或濫用) 系統資源的方式舉例來說,假設 可與伺服器同步處理的熱門應用程式如果同步處理作業是以時鐘為依據 而每個應用程式執行個體則在晚上 11:00 進行同步, 都可能造成延遲時間過長 「阻斷服務。」請遵循下列鬧鐘使用最佳做法:
針對因重複鬧鐘觸發的任何網路要求,新增隨機 (抖動) 值:
鬧鐘觸發時執行任何本機工作。「本機工作」是指不會觸及伺服器或需要伺服器資料的任何工作。
同時,排定含有網路要求的鬧鐘,以便在隨機時間點觸發。
盡量減少鬧鐘頻率。
避免不必要地喚醒裝置 (這項行為取決於鬧鐘類型,請參閱「選擇鬧鐘類型」一節)。
不要將鬧鐘觸發時間設定得比預期更準確。
使用
setInexactRepeating()
而不是setRepeating()
。 使用setInexactRepeating()
時,Android 會同步處理來自多個應用程式的重複鬧鐘,並同時觸發鬧鐘。這麼做可減少系統必須喚醒裝置的總次數,進而減少電池耗電量。自 Android 4.4 起 (API 級別 19),所有重複鬧鐘都是不精確鬧鐘。請注意,雖然setInexactRepeating()
比setRepeating()
更進步,但如果應用程式的每個例項幾乎同時存取伺服器,仍可能造成伺服器過載。因此,針對網路要求,請新增隨機性參數 設定鬧鐘盡量避免以時鐘時間設定鬧鐘。
以精確觸發時間為依據的週期性鬧鐘無法順利擴充。使用
ELAPSED_REALTIME
表示 。不同鬧鐘 請參閱下一節的詳細說明。