BroadcastReceiver 註冊於 AndroidManifest.xml(靜態註冊)或 Context.registerReceiver()(動態註冊)。動態註冊隨進程銷毀而失效;靜態註冊雖持久,但 Android 3.1+ 採用「停止狀態」機制:應用被強制停止或用戶從未啟動過,廣播不會喚醒組件。
AlarmManager.set() 設置鬧鐘,PendingIntent 指向 BroadcastReceiver。AlarmManager 設置的鬧鐘被清空(非 setExactAndAllowWhileIdle/setAlarmClock 亦會丟失)。BOOT_COMPLETED 廣播無法喚醒 Receiver 重註冊鬧鐘。<uses-permission android:name="android.permission.RECEIVE_BOOT_COMPLETED" />
<receiver
android:name=".BootReceiver"
android:enabled="true"
android:exported="true">
<intent-filter>
<action android:name="android.intent.action.BOOT_COMPLETED" />
<action android:name="android.intent.action.QUICKBOOT_POWERON" /> <!-- 部分廠商 -->
</intent-filter>
</receiver>
<receiver android:name=".AlarmReceiver" android:exported="false" />
public class BootReceiver extends BroadcastReceiver {
@Override
public void onReceive(Context context, Intent intent) {
if (Intent.ACTION_BOOT_COMPLETED.equals(intent.getAction())
|| "android.intent.action.QUICKBOOT_POWERON".equals(intent.getAction())) {
// 啟動 Service 重排程,避免主線程超時
context.startService(new Intent(context, RescheduleService.class));
}
}
}
public class RescheduleService extends IntentService {
public RescheduleService() { super("RescheduleService"); }
@Override
protected void onHandleIntent(Intent intent) {
// 從 SharedPreferences/DB 讀取用戶設置的提醒時間
List reminders = ReminderRepository.getAll(this);
AlarmManager am = (AlarmManager) getSystemService(ALARM_SERVICE);
for (Reminder r : reminders) {
PendingIntent pi = PendingIntent.getBroadcast(this, r.id,
new Intent(this, AlarmReceiver.class).putExtra("reminder_id", r.id),
PendingIntent.FLAG_UPDATE_CURRENT | PendingIntent.FLAG_IMMUTABLE);
// Android 6+ Doze 模式兼容
if (Build.VERSION.SDK_INT >= 23) {
am.setExactAndAllowWhileIdle(AlarmManager.RTC_WAKEUP, r.triggerAtMillis, pi);
} else if (Build.VERSION.SDK_INT >= 19) {
am.setExact(AlarmManager.RTC_WAKEUP, r.triggerAtMillis, pi);
} else {
am.set(AlarmManager.RTC_WAKEUP, r.triggerAtMillis, pi);
}
}
}
}
public class AlarmReceiver extends BroadcastReceiver {
@Override
public void onReceive(Context context, Intent intent) {
int reminderId = intent.getIntExtra("reminder_id", -1);
// 彈通知、響鈴、啟動 Activity 等
NotificationHelper.showReminder(context, reminderId);
}
}
| 方案 | 適用場景 | 優勢 |
|---|---|---|
| WorkManager | 可延遲、需保證執行的後台任務(同步、備份、日誌上傳) | 系統級調度、電池優化友好、支援約束條件、跨版本一致 |
| JobScheduler (API 21+) | 周期性、網絡相關任務 | 系統批次調度、省電 |
| Foreground Service + AlarmManager | 精確鬧鐘、音樂播放、導航 | 用戶可見、不易被殺 |
總結:靜態註冊 BOOT_COMPLETED + Service 重排程 是傳統 BroadcastReceiver 方案的正確打開方式。新項目優先評估 WorkManager,僅在需精確到秒的鬧鐘場景保留 AlarmManager 方案。