1. Doze模式下,WorkManager setInitialDelay設置小于15分鐘,被系統強制到15分鐘執行,怎么辦 ?
Android 擁有兩項省電功能,通過管理設備未連接電源時應用的行為來延長用戶電池續航時間:低電耗模式 (Doze) 和應用待機模式 (App Standby)。 低電耗模式 通過延遲設備長時間未使用時應用的后臺 CPU 和網絡活動來降低電池消耗。 應用待機模式 延遲沒有近期用戶活動的應用的后臺網絡活動。
設備處于低電耗模式時,應用對某些耗電資源的訪問會被延遲,直到維護窗口。 具體限制列在電量管理限制中。
低電耗模式和應用待機模式管理在 Android 6.0 或更高版本上運行的所有應用的行為,無論它們是否專門針對 API 級別 23。為了幫助確保為用戶提供最佳體驗,請在低電耗模式和應用待機模式下測試您的應用,并對您的代碼進行任何必要的調整。以下部分提供詳細信息。
2. 了解低電耗模式(Doze)
如果用戶將設備拔下電源并長時間靜置,且屏幕關閉,設備就會進入低電耗模式。在低電耗模式下,系統會嘗試通過限制應用對網絡和 CPU 密集型服務的訪問來節省電池電量。它還會阻止應用訪問網絡,并延遲其作業、同步和標準鬧鐘。
系統會定期短暫退出低電耗模式,讓應用完成其延遲的活動。在此 維護窗口 期間,系統會運行所有待處理的同步、作業和鬧鐘,并允許應用訪問網絡。
維護窗口結束后,系統會再次進入低電耗模式,暫停網絡訪問并延遲作業、同步和鬧鐘。隨著時間的推移,系統安排維護窗口的頻率會降低,有助于在設備未充電且長時間不活動的情況下減少電池消耗。
當用戶通過移動設備、打開屏幕或連接充電器來喚醒設備時,系統會退出低電耗模式,所有應用都會恢復正常活動。
3. 低電耗模式限制
設備處于低電耗模式時,系統會對您的應用施加以下限制:
- 暫停網絡訪問。
- 忽略喚醒鎖。
- 將標準 AlarmManager 鬧鐘(包括 setExact() 和 setWindow())延遲到下一個維護窗口。
- 如果您需要在低電耗模式下觸發鬧鐘,請使用 setAndAllowWhileIdle() 或 setExactAndAllowWhileIdle()。
- 通過 setAlarmClock() 設置的鬧鐘會正常觸發。系統會在這些鬧鐘觸發前不久退出低電耗模式。
- 不執行 Wi-Fi 掃描。
- 不允許同步適配器運行。
- 不允許 JobScheduler 運行。
?? WorkManager 內部使用 JobScheduler,因此 WorkManager 任務不會運行。
具體詳見 Android Developer | doze-standby
4. Doze模式下,WorkManager 為何無法精確時間執行 ?
通過上文,我們可以知道,在Doze模式下,WorkManager 任務不會運行,只有在兩個Doze間隔期間,系統會定期短暫退出低電耗模式,讓應用完成其延遲的活動。在此 維護窗口 期間,系統會運行所有待處理的同步、作業和鬧鐘,并允許應用訪問網絡。 這個時候WorkManager的任務才會被執行。但是兩個Doze之間休眠時間的間隔是不確定的,所以Doze模式下,WorkManager無法精確時間被執行。
且如果WorkManager.setInitialDelay設置的時間小于15分鐘,會被系統強制替換為15分鐘。
那如果我就想10分鐘后執行,需要怎么辦呢 ?
5. Doze模式下,如何精確時間執行 ?
這個時候,就需要使用AlarmManager的setExactAndAllowWhileIdle方法了。 (雖然WorkManager在Android低版本上也是用的AlarmManager,但是并沒有使用AlarmManager的setExactAndAllowWhileIdle方法)
那么如何使用呢 ?
5.1 ??聲明權限?
<uses-permission android:name="android.permission.SCHEDULE_EXACT_ALARM" />
5.2 調用前需驗證是否已授權
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.S) {AlarmManager alarmManager = (AlarmManager) getSystemService(ALARM_SERVICE);if (!alarmManager.canScheduleExactAlarms()) {// 引導用戶前往設置頁授權Intent intent = new Intent(Settings.ACTION_REQUEST_SCHEDULE_EXACT_ALARM);intent.setData(Uri.parse("package:" + getPackageName()));startActivity(intent);return;}
}
5.3 創建 BroadcastReceiver? : ??接收鬧鐘觸發事件
public class AlarmReceiver extends BroadcastReceiver {@Overridepublic void onReceive(Context context, Intent intent) {// 執行任務邏輯(如啟動服務、發送通知)Log.d("Alarm", "Triggered at exact time!");}
}
別忘了注冊 Receiver?
<receiver android:name=".AlarmReceiver" android:exported="false"/>
5.4 設置精確鬧鐘?
// 獲取 AlarmManager
AlarmManager alarmManager = (AlarmManager) getSystemService(Context.ALARM_SERVICE);// 創建 Intent 指向 BroadcastReceiver
Intent intent = new Intent(this, AlarmReceiver.class);
PendingIntent pendingIntent = PendingIntent.getBroadcast(this, 0, intent, PendingIntent.FLAG_UPDATE_CURRENT | PendingIntent.FLAG_IMMUTABLE
);// 設置觸發時間(例如 10 分鐘后)
long triggerTime = System.currentTimeMillis() + 10 * 60 * 1000;// 根據版本選擇方法
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {alarmManager.setExactAndAllowWhileIdle(AlarmManager.RTC_WAKEUP, // 使用 UTC 時間并喚醒設備triggerTime, pendingIntent);
} else if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT) {alarmManager.setExact(AlarmManager.RTC_WAKEUP, triggerTime, pendingIntent);
} else {alarmManager.set(AlarmManager.RTC_WAKEUP, triggerTime, pendingIntent);
}
5.5 設置了AlarmManager,如果系統時間變更了,是不是鬧鐘在現實世界響起的時間也會變 ?
- 若用戶將系統時間??調快 1 小時??(從 9:00 改為 10:00),鬧鐘會??立即觸發??(因為系統時間已達到目標值)。
- 若將系統時間??調慢 1 小時??(從 10:00 改為 9:00),鬧鐘會??延遲 1 小時觸發??(需等待系統時間再次達到 10:00)
可以監聽系統時間變化的廣播,然后更改鬧鐘
// 在 onResume() 中注冊
IntentFilter filter = new IntentFilter();
filter.addAction(Intent.ACTION_TIME_CHANGED);
filter.addAction(Intent.ACTION_TIMEZONE_CHANGED);
registerReceiver(timeChangeReceiver, filter);// 在 onPause() 中注銷
unregisterReceiver(timeChangeReceiver);
5.6 STATE_DOZE和STATE_DOZE_SUSPEND,有什么區別 ?
在 Android 電源管理機制中,STATE_DOZE
和 STATE_DOZE_SUSPEND
是兩種不同的休眠狀態,其核心區別在于系統資源限制的嚴格程度和CPU活動狀態。以下是兩者的詳細對比:
6. STATE_DOZE 和 STATE_DOZE_SUSPEND
-
STATE_DOZE
(Doze 模式)- 首次引入于 Android 6.0(Marshmallow),目的是在設備閑置時(未充電、屏幕關閉、靜止狀態)減少后臺活動。
- 狀態描述:系統進入低功耗狀態,但CPU仍保持部分活動,周期性喚醒處理任務。
-
STATE_DOZE_SUSPEND
(Doze 掛起模式)- 強化版省電機制,于 Android 9.0(Pie) 引入,作為 Doze 的深度擴展。
- 狀態描述:系統進入更深度的休眠,CPU 完全停止運行,僅保留最低限度的硬件喚醒能力(如傳感器)。
6.1 CPU 與任務執行機制**
-
STATE_DOZE
:- CPU 未完全停止,而是周期性喚醒(維護窗口)。
- 維護窗口間隔:初始為每 30 分鐘喚醒一次,隨后間隔逐漸延長(如 1 小時、2 小時)。
- 任務處理:在維護窗口內,系統允許應用執行延遲的任務(如同步、AlarmManager 鬧鐘)。
-
STATE_DOZE_SUSPEND
:- CPU 完全掛起,無周期性喚醒。
- 任務凍結:所有后臺進程被強制暫停(通過 Linux cgroup 的
freezer
子系統),不再分配 CPU 時間片。 - 喚醒條件:僅通過外部事件觸發(如用戶操作、高優先級鬧鐘
setAlarmClock()
)。
6.2 網絡與后臺資源訪問
-
STATE_DOZE
:- 網絡限制:禁止后臺應用訪問網絡,僅維護窗口內開放。
- 部分豁免:高優先級 GCM 消息、短信/電話可臨時喚醒網絡。
-
STATE_DOZE_SUSPEND
:- 完全斷網:所有網絡訪問被禁止,包括 GCM 和短信(僅保留基礎通信服務如通話)。
- 硬件限制:Wi-Fi/GPS 掃描、傳感器數據采集均暫停。
6.3 鬧鐘與任務調度行為
-
STATE_DOZE
:- 標準鬧鐘延遲:
setExact()
或setWindow()
的鬧鐘被推遲至下一個維護窗口。 - 豁免鬧鐘:
setAndAllowWhileIdle()
或setExactAndAllowWhileIdle()
可在 Doze 下觸發(但受每分鐘 1 次的頻率限制)。
- 標準鬧鐘延遲:
-
STATE_DOZE_SUSPEND
:- 所有鬧鐘凍結:包括
AllowWhileIdle
類型的鬧鐘,僅setAlarmClock()
(用戶可見的鬧鐘)可喚醒設備。 - 任務調度失效:JobScheduler 和 WorkManager 任務被無限期推遲,直至退出 SUSPEND 狀態。
- 所有鬧鐘凍結:包括
6.4 Wakelock 處理
-
STATE_DOZE
:- 部分屏蔽:普通 WakeLock 被忽略(如
PARTIAL_WAKE_LOCK
),但高優先級服務(如媒體播放)可能被豁免。
- 部分屏蔽:普通 WakeLock 被忽略(如
-
STATE_DOZE_SUSPEND
:- 完全無效:所有 WakeLock 被強制釋放,無法阻止 CPU 掛起。
6.5 持續時間與退出機制
-
STATE_DOZE
:- 動態維護窗口:窗口間隔隨閑置時間延長而增加(30 分鐘 → 1 小時 → 數小時)。
- 退出條件:屏幕點亮、設備移動或充電。
-
STATE_DOZE_SUSPEND
:- 持續掛起:無周期性窗口,直至外部事件喚醒。
- 退出條件更嚴格:僅用戶交互(如按鍵)、
setAlarmClock()
鬧鐘或充電可喚醒。
6.6 對比總結
特性 | STATE_DOZE | STATE_DOZE_SUSPEND |
---|---|---|
CPU 狀態 | 周期性喚醒(維護窗口) | 完全停止 |
網絡訪問 | 僅維護窗口開放 | 完全禁止 |
鬧鐘執行 | 允許 AllowWhileIdle 類型 | 僅 setAlarmClock() 有效 |
任務調度 | 維護窗口內執行 | 完全凍結 |
Wakelock 有效性 | 部分豁免 | 全部釋放 |
退出條件 | 移動/亮屏/充電 | 僅用戶操作或高優先級鬧鐘 |
省電強度 | 中度 | 極端 |
6.7 適配建議
- 后臺任務:
- 使用
WorkManager
或JobScheduler
,其任務在STATE_DOZE
的維護窗口自動執行,但STATE_DOZE_SUSPEND
中仍會被凍結。
- 使用
- 實時性需求:
- 關鍵任務(如鬧鐘)優先用
setAlarmClock()
(用戶可見),或引導用戶將應用加入電池優化白名單。
- 關鍵任務(如鬧鐘)優先用
- 網絡依賴:
- 在
STATE_DOZE_SUSPEND
下網絡完全不可用,需通過高優先級 GCM 消息或短信喚醒設備(系統服務如電話/SMS 始終豁免)。
- 在
- 狀態檢測:
- 通過
PowerManager.isDeviceIdleMode()
檢測 Doze 狀態,但無法直接區分SUSPEND
(需依賴行為推斷)。
- 通過
📌 核心結論:
STATE_DOZE
是 周期性喚醒的輕度休眠,適合延遲容忍的任務;
STATE_DOZE_SUSPEND
是 CPU 完全停止的深度凍結,僅響應極高優先級事件。
開發者需針對兩者設計降級策略,確保關鍵功能在極端省電下仍可工作。
7. STATE_DOZE_SUSPEND狀態下設置setExactAndAllowWhileIdle有效嗎
在 Android 的 STATE_DOZE_SUSPEND
(深度掛起模式)狀態下,setExactAndAllowWhileIdle()
是無效的。以下是具體原因和機制分析:
7.1 STATE_DOZE_SUSPEND
的特性
- CPU 完全凍結:
此狀態下系統進入深度休眠,CPU 完全停止運行,所有后臺進程被強制暫停,僅保留基礎硬件喚醒能力(如高優先級鬧鐘或物理按鍵)。 - 網絡與任務凍結:
所有網絡訪問被禁止,JobScheduler
、WorkManager
等后臺任務被無限期推遲,標準鬧鐘和AllowWhileIdle
類鬧鐘均被凍結。
7.2 setExactAndAllowWhileIdle()
的限制
- 僅適用于普通 Doze 模式:
setExactAndAllowWhileIdle()
設計用于STATE_DOZE
(普通休眠),可在維護窗口外觸發(但受每分鐘 1 次的頻率限制)。但在STATE_DOZE_SUSPEND
下,系統無周期性喚醒機制,導致該方法失效。 - 深度省電下的喚醒條件:
僅以下事件可喚醒STATE_DOZE_SUSPEND
:- 用戶主動操作(如點亮屏幕)
- 高優先級鬧鐘
setAlarmClock()
(系統會提前退出休眠并顯示通知) - 設備充電。
7.3 替代方案
若需在 STATE_DOZE_SUSPEND
下可靠觸發任務,需采用以下策略:
setAlarmClock()
:- 用于用戶可見的精確鬧鐘(如鬧鐘應用),系統會強制退出休眠并顯示通知欄圖標。
- 無需特殊權限,但用戶感知明顯。
- 引導用戶加入白名單:
- 用戶手動在 設置→電池優化 中將應用設為“未優化”,可部分豁免限制(非完全保障,廠商兼容性差異大)。
- 前臺服務(Foreground Service):
- 通過持續通知欄服務維持進程活躍性,但需合理說明用途以避免被系統限制或用戶關閉。
7.4 總結:不同鬧鐘方法在 Doze 模式下的有效性
鬧鐘方法 | STATE_DOZE (普通休眠) | STATE_DOZE_SUSPEND (深度掛起) |
---|---|---|
setExact() / setWindow() | ? 延遲至維護窗口 | ? 完全凍結 |
setAndAllowWhileIdle() | ? 可觸發(精度低) | ? 無效 |
setExactAndAllowWhileIdle() | ? 可觸發(秒級誤差) | ? 無效 |
setAlarmClock() | ? 立即觸發 | ? 強制喚醒(需用戶可見通知) |
結論:
在STATE_DOZE_SUSPEND
下,setExactAndAllowWhileIdle()
無法觸發。若需極端省電模式下的可靠性,應優先使用setAlarmClock()
或引導用戶設置白名單)。
8. 更多內容
有關WorkManager基礎的概念和使用,詳見 : Android WorkManager的概念和使用