개요
AlarmManager는 앱과 안드로이드 시스템 알람 서비스의 연결해 주는 역할을 한다.
사용자가 지정한 시간에 앱에게 broadcast를 보내고 설정된 작업들을 실행한다.
구글은 배터리 사용 관련해서 AlarmManager에 제약을 걸었다.
알람은 더 이상 지정한 정확한 시간에 울리지 않는다.
즉, 시스템이 기기 상태에 따라 최적화된 시간으로 알람을 미룰 수 있다.
사용자가 기기를 재부팅 한 뒤에는 설정되어 있던 모든 알람이 취소된다.
그래서 반드시 재부팅 뒤에는 설정되어 있던 모든 알람을 다시 설정해야 한다.
사용방법
- BroadcastReceiver()를 상속받은 새로운 클래스 생성
- 리시버로 전달될 Intent 구현
- 알람은 일정 시간 뒤에 실행되기에 PendingIntent 구현
- AlarmManager의 API 사용하여 알람을 설정
- 알람 취소 방법
- 기기 재부팅 시 알람 재설정
1. BroadcastReceiver() 를 상속받은 새로운 클래스 생성
알람이 실행될 때 작업을 위한 리시버 클래스 즉, BroadcastReceiver() 를 상속받은 클래스를 생성한다.
실제 작업할 내용은 onReceive() 메서드에 담으면 된다.
class BookAlarmReceiver : BroadcastReceiver() {
override fun onReceive(context: Context, intent: Intent) {
// TODO 알람이 실행되었을 때 실제 동작하는 부분
}
}
2. 리시버로 전달될 Intent 구현
알람을 설정할 Activity나 Fragment에서 1번에서 설정한 리시버 클래스의 인텐트를 구현한다.
val bookAlarmIntent = Intent(requireContext(), BookAlarmReceiver::class.java)
3. 알람은 일정 시간 뒤에 실행되기에 PendingIntent 구현
알람은 일정 시간이 흐른 뒤에 실행되기 때문에 2번에서 생성한 인텐트를 통해 PendingIntent를 구현
val bookAlarmPendingIntent = PendingIntent.getBroadcast(
requireContext(),
0,
bookAlarmIntent,
PendingIntent.FLAG_UPDATE_CURRENT
)
파라미터 설명 PendingIntent.getBroadcast(1, 2, 3, 4)
- context: Context
- requestCode: Int - 해당 알람에 해당 고유한 ID이다. 알람을 여러개 생성 시 다른 ID를 할당해야 한다.
- intent: Intent - 리시버로 전달될 Intent
- flags: Int - 새롭게 또는 존재하는 PendingIntent 에 대한 처리 방침으로 5개의 flag가 있다.
flags | 설명 |
FLAG_ONE_SHOT | 한번만 실행 |
FLAG_NO_CREATE | 생성된 PendingIntent가 없으면 null을 반환하고, 아니면 생성 |
FLAG_CANCEL_CURRENT | PendingIntent가 이미 등록되어 있으면 취소하고, 새로운 PendingIntent 생성 |
FLAG_UPDATE_CURRENT | PendingIntent가 이미 등록되어 있으면 유지하고, Intent의 extra data는 새로 전달된 Intent로 교체 |
FLAG_IMMUTABLE | 생성된 PendingIntent는 변하지 않음. 새로운 인텐트의 arguments들은 무시 |
4. AlarmManager의 API 사용하여 알람을 설정
- 알람을 실행하는 방법은 두가지로 한번만 동작하는 것과 주기적으로 동작 하는 알람이 있다.
- 알람 유형에 따라 시간을 설정할 수 있다.
- 실제 경과 시간(ELAPSED_REALTIME) : 시스템 부팅 이후 시간을 참조, 시간대/언어의 영향을 받지 않음(경과 시간에 기반한 알람 적합)
- 실시간 시계(RTC) : 실제 시간을 참조, 사용자가 시간 설정을 변경한 경우 그 시간을 따라감
알람 유형 | 설명 |
ELAPSED_REALTIME_WAKEUP | 기기 부팅된 후 경과 시간 기반으로 인텐트 실행, 절전모드 해제하여 디바이스를 깨움 |
ELAPSED_REALTIME | 기기 부팅된 후 경과 시간 기반으로 인텐트 실행 |
RTC_WAKEUP | 지정된 시간에 인텐트 실행, 절전모드 해제하여 디바이스를 깨움 |
RTC | 지정된 시간에 인텐트 실행 |
4-1. 한번만 동작하는 알람 (실제 경과 시간)
// 1분 뒤에 기기 절전모드 해제하여 한번 알람을 실행
alarmManager.set(
AlarmManager.ELAPSED_REALTIME_WAKEUP,
SystemClock.elapsedRealtime() + 60 * 1000,
bookAlarmPendingIntent
)
alarmManager.set(1, 2, 3) 파라미터 설명
- type: Int - 알람 유형으로 '실시간 시계(RTC)'와 '실제 경과 시간(ELAPSED_REALTIME)'가 있다. (자세한 설명은 하단에 있음)
- triggerAtMillis: Long - 알람을 실행할 시간으로 milliseconds 단위로 입력
- operation: PendingIntent - 알람이 울렸을 때 실행할 작업을 정의한 pendingIntent
4-2. 주기적으로 동작하는 알람 (실제 경과 시간)
// 1시간 뒤에 알람이 울리고, 1시간 단위로 주기적 알람을 실행 (기기 절전모드 해제하지 않음)
alarmManager.setInexactRepeating(
AlarmManager.ELAPSED_REALTIME,
SystemClock.elapsedRealtime() + AlarmManager.INTERVAL_HOUR,
AlarmManager.INTERVAL_HOUR,
bookAlarmPendingIntent
)
alarmManager.setInexactRepeating(1, 2, 3, 4) 파라미터 설명 / (1, 2, 4)는 위와 동일
- intervalMillis: Long - 주기적으로 실행할 시간으로 milliseconds 단위로 입력
4-3. 한번만 동작하는 알람 (실시간 시계)
// 2시30분에 울리는 알람 시간 설정
val calendar: Calendar = Calendar.getInstance().apply {
timeInMillis = System.currentTimeMillis()
set(Calendar.HOUR_OF_DAY, 14)
set(Calendar.MINUTE, 30)
}
// 2시30분에 기기 절전모드 해제하여 한번 알람을 실행
alarmManager.set(
AlarmManager.RTC_WAKEUP,
calendar.timeInMillis,
bookAlarmPendingIntent
)
4-4. 주기적으로 동작하는 알람 (실시간 시계)
// 1분 뒤 기기 절전모드 해제하여 하루 단위 주기적 알람을 실행
alarmManager.setInexactRepeating(
AlarmManager.RTC_WAKEUP,
System.currentTimeMillis() + 60 * 1000,
AlarmManager.INTERVAL_DAY,
bookAlarmPendingIntent
)
5. 알람 취소
설정되어 있는 알람을 취소하는 방법은 AlarmManager.cancel() 호출하여 취소할 PendingIntent 전달
alarmManager.cancel(bookAlarmPendingIntent)
6. 기기 재부팅 시 알람 재설정
- 기기가 종료되면 모든 알람이 취소됨
- 해결 방법으로는 재부팅 시 자동으로 기존 알람 다시 설정
6-1. 애플리케이션 AndroidManifest.xml 파일에서 RECEIVE_BOOT_COMPLETED 권한 설정
<uses-permission android:name="android.permission.RECEIVE_BOOT_COMPLETED" />
이 권한을 사용하면 시스템 부팅이 끝난 후에 앱은 브로드캐스팅되는 ACTION_BOOT_COMPLETED 을 받음(최소 한 번 이상 앱을 실행한 경우에만 동작)
6-2. BroadcastReceiver 를 구현하여 브로드캐스트 받는다
// 알람 재설정을 위한 리시버를 새로 생성하거나, 기존에 생성한 리시버를 사용할 수 있다.
// 아래 예제에서는 기존 리시버 사용하여 알람 재설정
class BookAlarmReceiver : BroadcastReceiver() {
override fun onReceive(context: Context, intent: Intent) {
if (intent.action == "android.intent.action.BOOT_COMPLETED") {
// TODO 재부팅 시 기존 알람 재설정하는 부분
} else {
// TODO 알람이 실행되었을 때 실제 동작하는 부분
}
}
}
6-3. AndroidManifest.xml 파일에서 ACTION_BOOT_COMPLETED 작업 필터링 하는 인텐트와 함께 수신기 추가
<receiver
android:name=".BookAlarmReceiver"
android:enabled="true"
android:exported="false">
<intent-filter>
<action android:name="android.intent.action.BOOT_COMPLETED" />
</intent-filter>
</receiver>
알람이 얼마나 정확해야 하는가
- 알람 실행 시 위에서 설명한 방법은 시스템 환경에 알맞게 대략적인 시간에 실행되는 알람이다
- 위 방법은 Android에서 귄하는 방식으로 여러개의 부정확한 반복 알람을 동기화하고 동시에 실행하기에 배터리 소모를 줄일 수 있다
- 알람을 정확한 시간에 실행하는 방법
- 한번 실행 시 : setExact() 사용
- 주기적 실행 시 : setRepeating() 사용
Doze(잠자기) 와 App Standby(앱 대기)가 알람에 미치는 영향
- Android 6.0(API 23)부터 기기 배터리 수명 연장을 위해 Doze와 App Standby 도입됨
- 기기가 Doze(잠자기)모드일 때 모든 표준 알람은 기기가 Doze모드를 종료하거나 유지보수 기간이 시작될 때까지 지연됨
- 기기가 Doze모드일 때 알람을 실행하는 방법은 setAndAllowWhileIdle() 또는 setExactAndAllowWhileIdle() 사용
- 앱이 Idle(유휴상태)이면 App Standby모드로 전환됨, 즉 사용자가 일정 시간 앱을 사용하지 않았고 앱에 foreground process가 없다는 의미이다
- 기기가 App Standby(앱 대기)일 때 알람은 Doze모드와 같이 알람이 연기됨
- 이 제한은 앱이 더 이상 Idle가 아니거나 기기가 전원에 연결되어 있을 때 해제됨
참고
'Android' 카테고리의 다른 글
[Android] Retrofit2 사용하여 API 통신하기 (0) | 2021.08.03 |
---|---|
[Android] API Key 관리 (key 쉽게 숨기는 방법) (0) | 2021.07.18 |
[Android] EditText에서 숫자 입력 시 1000단위 Comma(',')로 구분하여 보여주기 (Kotlin) (0) | 2021.06.03 |
[Android] Google Fitness API (0) | 2021.05.30 |
[Android] 안드로이드 스튜디오 단축키 모음 (0) | 2021.04.10 |