設定鬧鐘

鬧鐘 (以AlarmManager 類別) 可讓您執行 位於應用程式生命週期之外的時間型作業。 舉例來說,您可以使用鬧鐘來啟動長時間執行的作業,例如: 每天開始使用服務一次,下載天氣預報資訊。

鬧鐘具有以下特性:

  • 可讓您按照設定的時間和/或間隔啟動意圖。

  • 你可以搭配廣播接收器使用排程 工作或 用於執行其他事件的 WorkRequest 作業。

  • 這類函式會在應用程式之外運作,因此您可以透過這類函式 事件或動作 (即使裝置未執行) 睡著了

  • 可協助您盡量減少應用程式的資源需求。預約時間 作業,不必仰賴計時器或持續執行服務。

,瞭解如何調查及移除這項存取權。

設定非精確鬧鐘

如果應用程式設定了非精確鬧鐘,系統就會在特定時間點觸發警報 不精確鬧鐘可以保證 同時遵守省電限制,例如 打盹

開發人員可以運用下列 API 保證來自訂 非精確的鬧鐘放送方式

在指定時間後觸發鬧鐘

如果您的應用程式呼叫 set()setInexactRepeating(), 或 setAndAllowWhileIdle(), 鬧鐘在指定的觸發時間之前不會響起

在 Android 12 (API 級別 31) 以上版本中,系統會在其中叫用鬧鐘 的啟動時間 (除非已設定省電限制) 例如省電模式打盹

在指定時間範圍內傳送鬧鐘

如果應用程式呼叫 setWindow(),鬧鐘就不會響起 觸發時間。除非所有節約耗電量限制生效,否則鬧鐘會一直響起 從指定的觸發事件開始,在指定時間範圍內送達 讓應用程式從可以最快做出回應的位置 回應使用者要求

如果您的應用程式指定 Android 12 或以上版本,系統可能會延遲 觸發時間範圍不精確鬧鐘至少 10 分鐘適用對象 因此,600000 底下的 windowLengthMillis 參數值會裁剪為 600000

以大致頻率傳送週期性鬧鐘

如果您的應用程式呼叫 setInexactRepeating(), 系統會叫用多個鬧鐘:

  1. 第一個鬧鐘會在指定時間範圍內響起 指定的觸發時間
  2. 後續的鬧鐘通常會在指定時間範圍後響起 如畫面所示兩次連續叫用鬧鐘的時間可能有所不同。

設定精確鬧鐘

系統會在未來的確切時間點叫用精確鬧鐘

大多數應用程式都能透過非精確鬧鐘排定工作和活動, 符合幾個常見用途的方法如果應用程式的核心 功能取決於精確計時的鬧鐘,例如鬧鐘應用程式 或行事曆應用程式,則可設定確切的鬧鐘。

可能不需要精確鬧鐘的用途

以下列出不需要精確鬧鐘的常見工作流程:

在應用程式的生命週期中安排時間作業
Handler 類別包含數個優質 處理時間作業,例如每 n 秒時,在應用程式上線期間: postAtTime()postDelayed()。 請注意,這些 API 會耗用系統運作時間 而非即時
排定的背景作業,例如:更新應用程式和上傳記錄檔
WorkManager 可讓您安排具時效性的定期週期 工作。 您可以提供重複間隔和 flexInterval (至少 15 分鐘), 為工作定義精細的執行階段
在特定時間後所應採取的使用者指定動作 (即使系統處於閒置狀態)
請使用非精準鬧鐘。具體問題為 setAndAllowWhileIdle()
在特定時間後所應採取的使用者指定動作
請使用非精準鬧鐘。具體問題為 set()
指定時間範圍內可以進行的使用者指定動作
請使用非精準鬧鐘。具體問題為 setWindow()。 請注意,如果您的應用程式指定 Android 12 以上版本,最小 時間長度一律為 10 分鐘

設定精確鬧鐘的方式

應用程式可以使用下列其中一種方法設定精確鬧鐘。這些方法 讓較接近清單底部的項目獲得更多曝光機會 但需要更多系統資源。

setExact()

在任何確切時間叫用鬧鐘 (只要其他 省電模式無效。

除非應用程式運作方式,否則請使用此方法設定精確鬧鐘 時間對使用者而言至關重要

setExactAndAllowWhileIdle()

即使在省電模式下,仍能在近乎精確的時間啟動鬧鐘 並採取有效措施

setAlarmClock()

可以在未來的確切時間叫用鬧鐘。因為這些鬧鐘 對使用者的能見度很高,所以系統永遠不會調整運送時間。 系統會將這些警報視為最關鍵的警報,並留下低功耗 模式。

系統資源用量

系統觸發應用程式設定的精確鬧鐘時,裝置 會耗用大量資源,例如電池續航力 就會有節電模式此外,系統無法輕鬆批次處理這些要求 讓資源使用更有效率

我們強烈建議您建立不精確鬧鐘 如要延長執行時間,請使用以下方式排定時間: WorkManager 或 從鬧鐘的響起的 JobScheduler BroadcastReceiver。長時間工作 裝置處於「打盹」模式,請建立非精確鬧鐘 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_ALARMUSE_EXACT_ALARM 權限。

<manifest ...>
    <uses-permission android:name="android.permission.USE_EXACT_ALARM"/>
    <application ...>
        ...
    </application>
</manifest>

SCHEDULE_EXACT_ALARMUSE_EXACT_ALARM 權限時 信號的能力相同、給予方式不同,也各能支援 用途您的應用程式應使用精確鬧鐘,並宣告 SCHEDULE_EXACT_ALARMUSE_EXACT_ALARM 權限 (僅限面向使用者) 應用程式功能需要精確計時的動作。

USE_EXACT_ALARM

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 廣播。您的應用程式應實作廣播訊息 接收端 包括:

  1. 確認應用程式仍擁有特殊應用程式存取權。方法是呼叫 canScheduleExactAlarms()。 如果使用者已授予您的應用程式 之後便會立即撤銷該權限。
  2. 根據應用程式目前的狀態,重新安排應用程式需要的確切鬧鐘時間。 這個邏輯應該與應用程式收到 ACTION_BOOT_COMPLETED敬上 廣播。

請使用者授予 SCHEDULE_EXACT_ALARM 權限

該選項稱為「允許設定鬧鐘和提醒」
圖 1. 鬧鐘與提醒"特殊應用程式存取權 以便讓應用程式 鬧鐘。

您可以視需要將使用者傳送到鬧鐘與系統中的提醒畫面 如圖 1 所示若要這樣做,請完成下列步驟:

  1. 在應用程式使用者介面中,向使用者說明應用程式需要排定確切時間的原因 鬧鐘。
  2. 叫用含有 ACTION_REQUEST_SCHEDULE_EXACT_ALARM敬上 意圖動作

設定週期性鬧鐘

重複鬧鐘會讓系統定期通知應用程式 排程。

鬧鐘的設計品質不佳可能會導致電池耗電,並對裝置造成嚴重負載 伺服器因此,在 Android 4.4 (API 級別 19) 以上版本中,所有 重複鬧鐘就是不精確鬧鐘

週期性鬧鐘具有以下特性:

  • 鬧鐘類型。如需進一步討論,請參閱「選擇鬧鐘類型」。

  • 觸發時間。如果指定的觸發時間是過去的時間,鬧鐘會設在過去的時間 觸發。

  • 鬧鐘的間隔。例如每天、每小時或每 5 分鐘一次。

  • 觸發鬧鐘時觸發的待處理意圖。設定 使用相同的待處理意圖的第二個鬧鐘則會取代原始鬧鐘。

如要取消PendingIntent(),請 FLAG_NO_CREATEPendingIntent.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_MINUTESINTERVAL_DAY, 依此類推查看「AlarmManager」 查看完整清單

取消鬧鐘

視應用程式而定,您可能想加入取消鬧鐘的功能。 如要取消鬧鐘,請撥打 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 會 繼續執行工作,無需使用者手動重新啟動鬧鐘。

步驟如下:

  1. 設定 RECEIVE_BOOT_COMPLETED 加入應用程式資訊清單中的權限。這樣一來,應用程式就能接收 ACTION_BOOT_COMPLETED敬上 會在系統啟動完畢後播送 (只有在 應用程式已由使用者啟動至少一次):

    <uses-permission android:name="android.permission.RECEIVE_BOOT_COMPLETED"/>
  2. 實作 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.
            }
        }
    }
    
  3. 使用以下意圖篩選器,將接收器新增至應用程式的資訊清單檔案 篩選 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"。這表示接收端 除非應用程式明確啟用,否則不會呼叫 API。這樣一來, 並在不必要的情況下呼叫啟動接收器。您可以啟用接收器 (舉例來說,如果使用者設定鬧鐘),如下所示:

    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) 的裝置支援 打盹 模式; 有助於延長裝置電池續航力裝置啟動時,鬧鐘不會觸發 打盹模式 所有排定的鬧鐘都會延後,直到裝置退出「打盹」模式為止。如果需要 完成工作 可使用:

  • 設定精確鬧鐘

  • 使用專為執行而設計的 WorkManager API。 背景工作。您也可以指定系統應加快作業速度 工作會盡快完成若需更多資訊,請參閲 使用 WorkManager 安排工作

最佳做法

設計週期性鬧鐘時所做的任何選擇都可能造成後果 瞭解應用程式使用 (或濫用) 系統資源的方式舉例來說,假設 可與伺服器同步處理的熱門應用程式如果同步處理作業是以時鐘為依據 而每個應用程式執行個體則在晚上 11:00 進行同步, 都可能造成延遲時間過長 「阻斷服務。」使用鬧鐘時,請遵循下列最佳做法:

  • 針對符合以下條件的網路要求加入隨機 (時基誤差) 重複鬧鐘而觸發:

    • 在鬧鐘觸發時執行任何本機工作。「當地的公司」泛指任何由 不會連結到伺服器,也不需要伺服器中的資料

    • 同時,將包含網路要求的鬧鐘時間設為 並在某個隨機時間範圍內觸發

  • 請降低鬧鐘頻率。

  • 不要在非必要的情況下喚醒裝置 (此行為是由 鬧鐘類型,詳情請參閱「選擇鬧鐘類型」一文。

  • 不要將鬧鐘觸發時間設定得比預期更準確。

    使用 setInexactRepeating()敬上 而不是 setRepeating()。 使用 setInexactRepeating() 時, Android 會同步處理多個應用程式和火災的重複鬧鐘 相同的內容這樣可減少系統必須喚醒 從而降低耗電量。自 Android 4.4 起 (API 級別 19),所有重複鬧鐘都是不精確鬧鐘。注意事項 那時候 setInexactRepeating()敬上 等於超過 setRepeating(), 即使應用程式的每個執行個體都到達伺服器,伺服器仍會癱瘓伺服器 兩者相輔相成因此,針對網路要求,請新增隨機性參數 設定鬧鐘

  • 如果可以,請避免在時鐘時間響起。

    如果是基於精確觸發時間的重複鬧鐘,則該鬧鐘不會適當縮放。使用 ELAPSED_REALTIME 表示 。不同鬧鐘 請參閱下一節的詳細說明。