AlarmManager là một dịch vụ hệ thống Android cho phép ứng dụng thực thi tác vụ vào thời gian đã chỉ định, ngay cả khi ứng dụng không chạy hoặc thiết bị ở chế độ ngủ. Không giống như JobScheduler hay WorkManager, AlarmManager đảm bảo độ chính xác khi kích hoạt, khiến nó không thể thiếu cho báo thức, lời nhắc lịch và các tác vụ quan trọng về thời gian thực thi. Theo Android Developers, 2026, từ Android 4.4, setRepeating hoạt động như một lần lặp không chính xác và báo thức chính xác yêu cầu setExact hoặc setAlarmClock.
Những điểm chính
AlarmManager là một dịch vụ hệ thống Android cung cấp API để lên lịch tác vụ với thời gian thực thi chính xác hoặc xấp xỉ. Nó đã tồn tại từ phiên bản Android đầu tiên và vẫn là cách đáng tin cậy duy nhất để thực thi mã tại một thời điểm cụ thể, bất kể trạng thái của ứng dụng và thiết bị.
Nguyên lý hoạt động rất đơn giản: ứng dụng gửi đến hệ thống một PendingIntent với thời gian kích hoạt đã chỉ định. Khi đến thời gian đã chỉ định, hệ thống gửi Intent đến một BroadcastReceiver đã đăng ký hoặc khởi động Service. Ngay cả khi thiết bị ở chế độ ngủ, WakeLock cho phép bộ xử lý thức dậy và xử lý sự kiện.
Lĩnh vực ứng dụng chính của AlarmManager là các tác vụ yêu cầu thời gian chính xác: báo thức, lời nhắc lịch, khởi động các thao tác dài tại một giờ cụ thể. Đối với các tác vụ mà độ chính xác không quan trọng (đồng bộ hàng ngày), Google khuyên dùng WorkManager hoặc JobScheduler vì chúng tiết kiệm năng lượng hơn.
AlarmManager nhận PendingIntent và thời gian kích hoạt từ ứng dụng. Hệ thống lưu yêu cầu này vào bộ lập lịch nội bộ và đánh thức bộ xử lý tại thời gian đã chỉ định để gửi Intent. Nhà phát triển phải đăng ký BroadcastReceiver trước để xử lý Intent này.
AlarmManager hỗ trợ 4 loại báo thức: ELAPSED_REALTIME (thời gian từ khi khởi động, không đánh thức), RTC (thời gian thực, không đánh thức), ELAPSED_REALTIME_WAKEUP (thời gian từ khi khởi động, đánh thức thiết bị) và RTC_WAKEUP (thời gian thực, đánh thức). Các phiên bản WAKEUP cần thiết nếu tác vụ phải thực thi ngay cả khi thiết bị đang ngủ.
| Loại | Thời gian | Đánh thức | Ví dụ |
|---|---|---|---|
| ELAPSED_REALTIME | từ lúc khởi động | không | bộ đếm thời gian hoạt động |
| RTC | dấu thời gian Unix | không | ghi nhật ký |
| ELAPSED_REALTIME_WAKEUP | từ lúc khởi động | có | tác vụ định kỳ |
| RTC_WAKEUP | dấu thời gian Unix | có | báo thức |
Từ Android 12 (API 31), việc sử dụng báo thức chính xác (setExact) yêu cầu quyền SCHEDULE_EXACT_ALARM. Người dùng có thể thu hồi quyền này qua cài đặt. Đối với các ứng dụng cần báo thức chính xác có hiển thị (ví dụ: ứng dụng đồng hồ), USE_EXACT_ALARM được sử dụng, quyền này được cấp khi cài đặt.
AlarmManager cung cấp ba phương thức chính để lên lịch tác vụ. Việc chọn phương thức sẽ quyết định độ chính xác kích hoạt và tác động đến mức tiêu thụ điện năng của thiết bị.
set — phương thức cơ bản cho một lần kích hoạt không chính xác. Hệ thống có thể dịch chuyển thời gian lên đến vài phút để nhóm với các sự kiện khác. Phù hợp cho các tác vụ mà độ chính xác không quan trọng: lời nhắc mở ứng dụng.
setRepeating — phương thức cho các lần lặp định kỳ. Từ Android 4.4 (API 19), setRepeating đã trở nên không chính xác — khoảng thời gian có thể thay đổi. Hệ thống không còn đảm bảo một khoảng thời gian cố định. Thay vì setRepeating, nên sử dụng setExact với lên lịch lại hoặc WorkManager với PeriodicWorkRequest.
setExact — phương thức cho một lần kích hoạt chính xác. Hệ thống đánh thức thiết bị càng gần thời gian đã chỉ định càng tốt. setAlarmClock là một trường hợp đặc biệt của setExact, cũng hiển thị biểu tượng báo thức trên thanh trạng thái và có mức ưu tiên cao nhất trong tất cả các loại báo thức.
val alarmManager = getSystemService(Context.ALARM_SERVICE) as AlarmManager
// Báo thức chính xác một lần
val intent = Intent(this, AlarmReceiver::class.java)
val pendingIntent = PendingIntent.getBroadcast(
this, 0, intent,
PendingIntent.FLAG_IMMUTABLE
)
alarmManager.setAlarmClock(
AlarmManager.AlarmClockInfo(
targetTime, pendingIntent
),
pendingIntent
)
Một kịch bản điển hình là tạo lời nhắc hàng ngày vào một thời gian cụ thể. RTC_WAKEUP với setExact được sử dụng cho mục đích này. Khi được kích hoạt, BroadcastReceiver khởi chạy thông báo hoặc Service. Sau khi xử lý, tác vụ phải được lên lịch lại cho ngày hôm sau.
class ReminderReceiver : BroadcastReceiver() {
override fun onReceive(context: Context, intent: Intent?) {
val notificationManager =
context.getSystemService(Context.NOTIFICATION_SERVICE)
as NotificationManager
val notification = NotificationCompat.Builder(
context, "reminder_channel"
)
.setContentTitle("Lời nhắc")
.setContentText("Đến lúc hoàn thành tác vụ")
.setSmallIcon(R.drawable.ic_reminder)
.build()
notificationManager.notify(1001, notification)
}
}
Để lặp lại hàng ngày, setExact được sử dụng với tính toán thời gian kích hoạt tiếp theo. Cờ có thể được truyền trong Intent để xác định các loại lời nhắc khác nhau. Đảm bảo BroadcastReceiver được đăng ký trong AndroidManifest.xml với xử lý hành động WAKEUP.
fun scheduleDailyReminder(context: Context, hour: Int, minute: Int) {
val calendar = Calendar.getInstance().apply {
set(Calendar.HOUR_OF_DAY, hour)
set(Calendar.MINUTE, minute)
set(Calendar.SECOND, 0)
if (before(Calendar.getInstance())) {
add(Calendar.DAY_OF_MONTH, 1)
}
}
val alarmManager =
context.getSystemService(Context.ALARM_SERVICE) as AlarmManager
alarmManager.setExactAndAllowWhileIdle(
AlarmManager.RTC_WAKEUP,
calendar.timeInMillis,
pendingIntent
)
}
AlarmManager là một công cụ mạnh mẽ nhưng tiêu tốn nhiều năng lượng. Mỗi báo thức WAKEUP đưa thiết bị ra khỏi chế độ ngủ, tiêu thụ pin. Google khuyên nên giảm thiểu việc sử dụng báo thức chính xác và ưu tiên setExactAndAllowWhileIdle trên Android 6+ để giảm tác động đến Doze Mode. Đối với các tác vụ định kỳ không yêu cầu độ chính xác, hãy sử dụng WorkManager với PeriodicWorkRequest, không cần đánh thức thiết bị và không làm hao pin mỗi lần thực thi.
Để kiểm tra AlarmManager, hãy sử dụng TestAlarmManager từ Android Test Framework. Nó cho phép mô phỏng kích hoạt báo thức mà không cần chờ thời gian thực. Robolectric cung cấp ShadowAlarmManager, chặn các cuộc gọi set, setExact và setRepeating, cung cấp các phương thức để kích hoạt cưỡng chế và kiểm tra số lượng tác vụ đã lên lịch. Đối với kiểm thử đơn vị của BroadcastReceiver, hãy sử dụng Robolectric.getForegroundScheduler(). Các kiểm thử Espresso với IdlingResource chờ kích hoạt báo thức trong kiểm thử tích hợp cũng có sẵn.
Nếu ứng dụng của bạn sử dụng AlarmManager cho các tác vụ định kỳ không yêu cầu thời gian chính xác, hãy cân nhắc di chuyển sang WorkManager. PeriodicWorkRequest với khoảng thời gian tối thiểu 15 phút thay thế setRepeating, trong khi WorkManager đảm bảo thực thi sau khi khởi động lại, xử lý Doze Mode và không yêu cầu quyền SCHEDULE_EXACT_ALARM. Đối với các tác vụ quan trọng về thời gian (báo thức lúc 7 giờ sáng), AlarmManager vẫn là lựa chọn đúng đắn duy nhất. Chiến lược tối ưu là chỉ sử dụng AlarmManager cho báo thức với setAlarmClock và chuyển tất cả các tác vụ nền khác sang WorkManager.
Đối với các tác vụ định kỳ không yêu cầu độ chính xác, hãy sử dụng WorkManager với PeriodicWorkRequest. Đối với các tác vụ có thời gian chính xác nhưng không quan trọng phải kích hoạt ở chế độ ngủ — hãy sử dụng setExact mà không có WAKEUP. Và chỉ cho các báo thức cần đánh thức bắt buộc — hãy sử dụng setAlarmClock hoặc RTC_WAKEUP.
Điều quan trọng nữa là kiểm tra quyền SCHEDULE_EXACT_ALARM trên Android 12+. Nếu quyền không được cấp, setExact sẽ hoạt động như set thông thường (không chính xác). Sử dụng AlarmManager.canScheduleExactAlarms() để kiểm tra. Nếu thiếu quyền, bạn có thể yêu cầu người dùng vào cài đặt qua Intent(Settings.ACTION_REQUEST_SCHEDULE_EXACT_ALARM).
Một tính năng cực kỳ quan trọng của AlarmManager — tất cả các báo thức đã lên lịch đều bị đặt lại sau khi khởi động lại thiết bị. Để khôi phục chúng, bạn phải khai báo BroadcastReceiver xử lý Intent.ACTION_BOOT_COMPLETED và lên lịch lại tất cả các báo thức đang hoạt động trong phương thức onReceive. Nếu không làm điều này, người dùng sẽ mất tất cả lời nhắc sau khi tắt và bật điện thoại.
class BootReceiver : BroadcastReceiver() {
override fun onReceive(
context: Context,
intent: Intent
) {
if (intent.action ==
Intent.ACTION_BOOT_COMPLETED
) {
val prefs =
context.getSharedPreferences("alarms", 0)
val savedTime =
prefs.getLong("next_alarm", 0L)
if (savedTime > System.currentTimeMillis()) {
scheduleReminder(context, savedTime)
}
}
}
}
Trong thực tế, AlarmManager vẫn là giải pháp tốt nhất cho các ứng dụng báo thức, lịch, nhắc nhở uống thuốc và bất kỳ tác vụ nào mà thời gian thực thi quan trọng đối với người dùng. Đối với tất cả các kịch bản công việc nền khác, WorkManager hoặc JobScheduler được ưu tiên hơn.
Khi chọn giữa AlarmManager và WorkManager, hãy tuân theo quy tắc này: nếu người dùng yêu cầu rõ ràng nhắc nhở lúc 2:30 chiều — hãy sử dụng AlarmManager với setAlarmClock. Nếu tác vụ nên thực thi “khoảng một lần mỗi giờ” — WorkManager với PeriodicWorkRequest sẽ tiết kiệm năng lượng và đáng tin cậy hơn.
Các câu hỏi thường gặp
Cả hai phương thức đều đảm bảo kích hoạt chính xác, nhưng setAlarmClock còn hiển thị biểu tượng báo thức trên thanh trạng thái và thông báo cho hệ thống rằng đó là báo thức của người dùng. Trên Android 6+, setAlarmClock có khả năng miễn nhiễm với Doze Mode, trong khi setExact có thể bị trì hoãn.
Sau khi khởi động lại, tất cả các báo thức đã lên lịch đều bị đặt lại. Để khôi phục chúng, bạn phải đăng ký BroadcastReceiver cho hành động BOOT_COMPLETED và lên lịch lại tất cả các tác vụ trong onReceive. Nếu không làm điều này, sẽ không có báo thức nào kích hoạt sau khi bật thiết bị.
Từ API 19, setRepeating đã trở nên không chính xác — hệ thống có thể dịch chuyển khoảng thời gian để tiết kiệm năng lượng. Thay vào đó, hãy sử dụng setExact với lên lịch lại thủ công hoặc WorkManager với PeriodicWorkRequest, cung cấp hành vi dự đoán trước hơn.
Đối với setExact, cần có quyền SCHEDULE_EXACT_ALARM, người dùng có thể cấp hoặc thu hồi trong cài đặt. Đối với setAlarmClock có hiển thị giao diện đồng hồ, USE_EXACT_ALARM được sử dụng, quyền này được cấp tự động khi cài đặt từ cửa hàng.
Không, AlarmManager luôn hoạt động thông qua PendingIntent. Nó có thể là PendingIntent.getBroadcast cho BroadcastReceiver, PendingIntent.getService cho Service hoặc PendingIntent.getActivity cho Activity. Không có PendingIntent, hệ thống không thể gửi sự kiện đến ứng dụng.
Tổng kết
Chúng tôi sẽ phát triển ứng dụng di động chìa khóa trao tay
IT Sectr tạo các ứng dụng iOS và Android cho các công ty khởi nghiệp và doanh nghiệp từ năm 2017. Chúng tôi sẽ tư vấn và đề xuất giải pháp tốt nhất cho bạn.
Đọc thêm