안드로이드 알람 어플리케이션의 꽃 Alarm Manager 에 대해 알아보기전에 두가지에 대해 먼저 알아보겠습니다.
Doze 모드
Doze 는 기기의 충전선을 뽑고 화면을 끈채 기기를 사용하지 않으면, Android가 배터리를 아끼기 위해 백그라운드 작업을 단계적으로 제한 하는 절전 모드 입니다. 배터리를 아끼려고 네트워크,CPU,일반 알람,Job/WorkManager 작업을 전부 잠시 미뤄둡니다. 시간이 지나면 잠깐 깨워 ( maintenance window ) 밀린 일만 몰아서 처리하고 다시 잠듭니다.
Doze는 두가지 진입 단계가 있습니다.
1) Light Doze (화면 OFF 직후 + 충전X)
Light Doze 는 네트워크 차단, Job/Sync 작업을 연기 하고 몇 분 간격으로 한꺼번에 처리하는 유지와 해제 주기를 반복 합니다.
즉, N 분 간격(기기별 1~15 분) 유지, 스마트폰 화면을 켜면 Doze 가 해제가 됩니다.
2) Deep Doze (보통 화면 OFF 30분 후 + 충전X + 폰 이동 정지(가속도계 무동작)) -
위 Light Doze 제약을 가지고 있으면서 Doze 유지 - 해제 간격이 배수로 증가합니다. (30분 -> 60분 -> 120분..)
앱 테스트 진행 시 Doze 진입 방법 (usb 연결 기기 or 에뮬레이터)
#1. 충전선 뽑힌 상태인지 확인
adb shell dumpsys battery unplug
#2. 강제로 Deep Doze 진입
adb shell cmd deviceidle force-idle
#3. 현재 상태 확인
adb shell dumpsys deviceidle | head -n 20
#4. 테스트 후 정상 복귀
adb shell cmd deviceidle unforce
위와 같은 방법으로 Android Studio에서 Doze 상태를 테스트할 수 있습니다.
Logcat을 통해 Doze 진입 시점과 예약된 알람이 Doze를 해제하고 실제로 알람이 트리거된 시점을 확인하여, 예약한 알람 시간과의 오차 범위를 파악할 수 있습니다.

제조사 별 제약
안드로이드 OS는 다양한 기기에서 사용이 됩니다. 하지만 안드로이드 OS 기반으로 하드웨어 제조사에 따라 프로세스 관리가 다른 경우가 있습니다. 대체로 백그라운드 프로세스를 강제로 종료하거나 브로드캐스트를 차단 하는 경우가 있다고 합니다.
대외적으로 알려진 사실은 다음과 같습니다.
| 제조사 | 특징적인 제약 | 해결을 위한 사용자 가이드 |
| Xiaomi | “Battery saver”, “MIUI Optimization”, “AutoStart” 스위치가 3중으로 백그라운드 브로드캐스트를 막아 알람 미전달 사례 다수 | 앱 첫 실행 시 ACTION_REQUEST_IGNORE_BATTERY_OPTIMIZATIONS 인텐트로 화이트리스트 안내, AutoStart 설정 페이지 딥링크 제공 |
| Huawei | PowerGenie·System Manager가 화면 OFF 상태 앱을 1~3 분 내 강종 | 배터리 사용량 최적화” → “모든 앱” → 앱 선택 후 ‘허용’을 유도. 푸시 알람은 FCM high-priority로 대체 |
| Samsung (One UI) | 셋팅 -> 사용하지 않는 앱 관리로 인한 프로세스 사망 | ACTION_REQUEST_IGNORE_BATTERY_OPTIMIZATIONS 외에도 “앱 사용 중지 안함” 설정 위치 안내 필요 |
정확한 알람 기능이 어떤 상황에서든지 잘 동작하기 바란다면, Alarm Manager 가 Doze 를 어떻게 깨워야 할지 확실히 알고 대처를 하거나, 제조사에 따른 사용자 가이드를 제공하거나 Alarm Manager 세팅 외에 다양한 준비가 필요합니다.
1. AlarmManager 의 강력한 특징
- 앱의 생명주기를 완전히 벗어나도 알람이 가능합니다.
- setAlarmClock() 및 setExactAndAllowWhileIdle() 사용시 앱이 죽어도 OS가 지정 시점에 인텐트를 전달이 가능합니다.
- 다만, Android 13 이후 부터 SCHEDULE_EXACT_ALARM 혹은 USE_EXACT_ALARM 권한을 허용해서 사용해야 Doze 상태에서 Doze 제한을 무시하는 알람 등록 (setAlarmClock(), setExactAndAllowWhileIdle() )을 사용하할 수 있습니다.
- 시계/캘린더/타이머 같은 실제 시간 맞춤: 배터리,네트워크 제약 안에서 정확 해야 할 알람은 여전히 AlarmManager가 유일한 옵션 입니다.
- 일반 앱이면 SCHEDULE_EXACT_ALARM 을 사용자에게 권한 허용을 얻어야 합니다. (정밀 알람 및 doze 에서 깨어나는 api 쓰고자 한다면)
- 알람 관련 앱이면 USE_EXACT_ALARM 권한을 자동으로 세팅할 수 있습니다. (단, 구글 스토어에서 알람앱으로 인정 받을 경우 활성화 됩니다.)

2. 알림 타입 4종
아래는 알람 타입에 관한 내용 입니다.
| 타입 | 절전 모드 무시 | 기준 시계 | 주 사용처 |
| ELAPSED_REALTIME | X | 부팅 이후 경과(ms) | 5 분 뒤 토큰 만료 등 |
| ELAPSED_REALTIME_WAKEUP | O (디바이스 깨움) | ↑ | 오프라인 백업 재시도 |
| RTC | X | UTC / 로컬 시간 | 타이머(앱이 깨어있을 때만) |
| RTC_WAKEUP | O | ↑ | 모닝콜 / 캘린더 알림 |
기기의 배터리를 생각하면 ELAPSED.. 를 사용하고 정확한 시각이 정말 필요한 경우에만 RTC + WAKEUP 조합을 씁니다.
alarmManager.setInexactRepeating(
RTC_WAKEUP,
firstTriggerAt,
AlarmManager.INTERVAL_DAY,
// PendingIntent
sender
)
하지만 setAlarmClock() 의 파라미터에 상수는 있지 않습니다. setAlarmClock 자체가 이미 RTC_WAKEUP 타입으로 동작하기 때문 입니다. 사용자에게 명확히 보이는 알람을 위한 API 이라서 이미 내부적으로 하나의 타입으로만 사용 됩니다.

3. API 메서드 별 알람 정확도
중요한 API 메서드만 나열했습니다.
| 메서드 | 정확도 | Doze 지연 여부 | 반복 기능 | 대표 용도 |
| setExact() | Exact | 지연 | ✕ | 캘린더 리마인더(오차 수분 이내) |
| setExactAndAllowWhileIdle() | Exact | 즉시 실행(15개/24h 제한, android 12 ~ 14 인 경우 72회 /1h ) | ✕ | 모닝콜 등 시간 초 단위 보장 |
| setRepeating() | Inexact(19+) | 지연 | O | 매일 09:00 백업 → WorkManager 권장 |
| setInexactRepeating() | Inexact | 지연 | O | 날씨 갱신·뉴스 동기화 |
| setAlarmClock() | Exact | 즉시 실행 | ✕ | 사용자에게 “다음 알람” UI 노출 |
setAlaramClock() 이 시간이 가장 정확하며 Doze 상관없이 즉시 실행 되는 특징이 있습니다. 하지만 그만큼 큰 단점이 존재합니다. 첫번째로 매우 강력한 api인 만큼 배터리 소모율이 매우 크기도 합니다. 두번째로 기본 UI 호출이 문제입니다. 상태바 아이콘이 자동으로 생겨버리거나 잠금화면에 다음 알림으로 생성이 됩니다. 심지어 일부 제조사에서는 시계 위젯에 까지 노출 됩니다. ( 사용자의 UX 를 중요시 하는 경우 골칫거리가 될 수 있습니다.)
setAlaramClock() 을 제외해서 다음 강력한 것은 setExactAndAllowWhileIdle() 입니다. setExactAndAllowWhileIdle() 의 경우 1시간에 72회 호출로 제한되어 있습니다. 그리고 setAlarmClock() 과 달리 기본 ui가 생성되지 않습니다.
그외 지목할 점은 setRepeating 과 setInexactRepeating 입니다. 이 두가지 API 는 반복 기능이 있습니다. 하지만 정확한 시간 알람에 사용하기에는 정확도가 떨어집니다. 그래서 일정 주기 반복되는 알람을 만드려면 api 자체 기능을 쓰는 것이 아니라 1회 알람시 새로운 알람을 이어서 바로 예약해서 반복적으로 유지 시킬 수 있습니다.

4. 알람 반복 등록하기
class MorningAlarmReceiver : BroadcastReceiver() {
override fun onReceive(context: Context, intent: Intent) {
//1. 알람이 울렸을 때 할 일 // 예: 모닝콜 노티·사운드
NotificationHelper.showMorningCall(context)
//2. 다음 알람 시각 계산
val nextTriggerAt = ZonedDateTime.now()
.plusDays(1)
.withHour(8).withMinute(0)
.withSecond(0).withNano(0)
.toInstant().toEpochMilli()
//3. 같은 PendingIntent 재사용해 알람 재등록
val alarmMgr = context.getSystemService(AlarmManager::class.java)
val pi = PendingIntent.getBroadcast(
context,
Intent(context, MorningAlarmReceiver::class.java),
PendingIntent.FLAG_UPDATE_CURRENT or PendingIntent.FLAG_IMMUTABLE
)
alarmMgr.setExactAndAllowWhileIdle(
AlarmManager.RTC_WAKEUP,
nextTriggerAt,
pi
)
}
}
alarm manager api 자체에 정확한 시간을 맞추는 반복 알람 기능이 없기 때문에 직접 알람을 재등록 해야 반복 알람 설정이 가능합니다. 그래서 일반 알람앱에 경우 onReceive 에서 첫번째로 사운드를 발생시키고 두번째로 notification 을 발생 시켜서 누르면 알람 화면으로 이동 시킵니다. 혹은 android.permission.SYSTEM_ALERT_WINDOW 을 사용자에게 허락 받아서 (사용자 설정 → “다른 앱 위에 표시”) 잘 이용하는 방법 등등이 있을 겁니다.

5. ACTION_REQUEST_IGNORE_BATTERY_OPTIMIZATIONS
<!-- Manifest.xml -->
<uses-permission
android:name="android.permission.REQUEST_IGNORE_BATTERY_OPTIMIZATIONS"/>
// 예시
val intent = Intent(Settings.ACTION_REQUEST_IGNORE_BATTERY_OPTIMIZATIONS).apply {
data = Uri.parse("package:$packageName")
}
startActivity(intent)
ACTION_REQUEST_IGNORE_BATTERY_OPTIMIZATIONS 는 Android 6.0(API 23) 부터 추가된 시스템 Intent 액션입니다. 이 인텐트를 실행하면 OS가 배터리 사용량 최적화 제외(Doze·앱 대기 모드 무시) 권한을 사용자에게 직접 묻는 다이얼로그를 띄웁니다. 사용자가 “허용”을 탭하면 해당 앱이 Doze 절전 제약에서 제외 백그라운드 작업·정확한 알람·네트워크 I/O 등이 중단 없이 동작할 수 있습니다.
하지만 ACTION_REQUEST_IGNORE_BATTERY_OPTIMIZATIONS 의 경우 구글 스토어는는 핵심 기능이 절전 차단이 없으면 제대로 작동 않음 인 앱만 허용합니다.

6. 기기 환경 변경 대응
<receiver
android:name=".AlarmRescheduler"
android:exported="false"
android:directBootAware="true">
<intent-filter>
<action android:name="android.intent.action.BOOT_COMPLETED"/>
<action android:name="android.intent.action.LOCKED_BOOT_COMPLETED"/>
<action android:name="android.intent.action.TIMEZONE_CHANGED"/>
<action android:name="android.intent.action.TIME_SET"/>
</intent-filter>
</receiver>
- ACTION_BOOT_COMPLETED
- 재부팅 뒤 한 번만 방송됩니다.
- 재부팅 시 모든 알람이 소멸하므로, AlarmManager.set*()로 다시 등록해야 합니다.
- ACTION_LOCKED_BOOT_COMPLETED
- Direct Boot(기기 잠금 상태) 도달 직후 발생합니다.
- 비밀번호 해제 전에도 울려야 하는 알람(모닝콜 등)을 다시 세팅할 때 사용합니다.
- <receiver android:directBootAware="true"> 로 명시해야 수신 가능
- ACTION_TIMEZONE_CHANGED
- 사용자가 타임존을 바꾸거나 DST(서머타임) 전환이 일어나면 방송됩니다.
- 알람을 “오전 9시” 같은 로컬 시각 기준으로 저장해 두었다면,
- 기존 알람 취소
- 새 타임존 기준 시각으로 재계산 → 재등록
를 해 주어야 다음 날에도 정확히 9시에 울립니다.
- ACTION_TIME_SET
- 사용자가 시계를 수동으로 조정하거나 네트워크 시간 동기화로 시스템 시각이 크게 변할 때 방송됩니다.
- 로직은 TIMEZONE_CHANGED와 동일: 저장해 둔 알람 시각을 다시 계산해서 재세팅 필요합니다.

7. 알람 누락 대비 전략
만약 알람이 누락 됬다면 혹시 모를 대비 전략도 준비할 수 있을 것 같습니다.
Backup 알람 : 메인 알람(정확) + 5 분 뒤 setAndAllowWhileIdle() 예비 알람 실행
WorkManager Fallback : OneTimeWorkRequest(Expedited)로 같은 시각 예약하기 → 정확 알람 실패 시 최소 몇 분 내 작동하기
누락감지 : 마지막 울림 시각을 기록하고 앱 실행 시 누락 여부 점검 → 가이드 재노출하기
FCM Fallback : 서버에서 FCM 을 기기로 보내서 예정 시간에 미리 doze 를 깨워놓고

8. 제조사별 알람 살리기
알람 앱에서 알람이 울리지 않는다는 불만은 제조사 배터리 관리(앱 킬러) 설정 원인이 꽤 있을거라고 생각합니다. 다행히도 https://dontkillmyapp.com/ 과 같은 이슈 정리 사이트가 있어서 많은 도움을 얻을 수 있습니다. 대표적인 예시로 삼성 기기의 경우
① 설정 ▸ 배터리 및 디바이스 케어 ▸ 배터리 ▸ 백그라운드 사용 제한 → (1) 깊은 절전 앱 / (2) 절전 앱 목록에서 제거
② 설정 ▸ 앱 ▸ (앱) ▸ 배터리 → 백그라운드 사용 허용
③ 오래된 모델은 배터리 ▸ 앱 절전 → “사용하지 않는 앱 절전” 끔
위와 같이 사용자에게 설정을 요구 하는 방법이 있습니다.

9. 정리
- 정확한 알람 및 doze 깨우기는 setExactAndAllowWhileIdle() / setAlarmClock() 가 강력하다.
- SCHEDULE_EXACT_ALARM or USE_EXACT_ALARM 가 있어야 Alarm Manager 100% 활용
- 반복 알람은 “정확 알람 → 다음 날 재예약” 패턴
- BOOT / TIMEZONE 변경 시 재등록 + Direct Boot 대응
- 제조사별 절전 예외 UX 필수
- Backup 알람 & 누락 감지로 위험도 보강
'안드로이드 Android' 카테고리의 다른 글
| 안드로이드 개발 (46) Process, Activity, Compose LifeCycle (2) | 2025.05.02 |
|---|---|
| 안드로이드 개발 (44) Room @Transaction 에 대해 알아보자 (1) | 2025.04.20 |
| 안드로이드 개발 (43) Compose Recomposition 최적화 (0) | 2025.04.18 |
| 안드로이드 개발 (42) Kotlin In Action 정리 - 6 (0) | 2023.02.24 |
| 안드로이드 개발 (41) Kotlin In Action 정리 - 5 (0) | 2023.02.19 |