Backgorund 작업 구분
Background 작업으로 AlarmManager나 WorkManager, Coroutine을 사용할 수 있는데 공식 문서를 보면 사용 구분에 따라 어떠한 기술을 사용해야 할지 가이드라인을 제공하고 있다.
즉시 (Immediate)
사용자와 상호작용 요구
- 앱의 Scope 내부에서만 유요한: Coroutine, RxJava
- 앱 외부 Scope에서도 유요한: WorkManager
- 미디어나 스마트키 위젯에 유요한: Foreground Service
정시 (Exact)
사용자와 상호작용 요구하지 않음
정확한 시간에 작동해야함
- AlarmManager
지연 (Deferred)
사용자와 상호작용 요구하지 않음
정확한 시간에 작동하지 않아도 됨
- WorkManager
정확한 시간에 동작하며 사용자와 상호작용 않는 작업은 AlarmManager 사용을 고려
AlarmManager
시스템 알람 서비스에 대한 접근과 알람을 통해 미래 특정 시점에 앱이 실행되도록 예약할 수 있게 만들어주는 클래스
알람이 울리면 등록된 Intent가 시스템에 의해 BroadCast 되고 앱이 시작된다.
단, 등록된 알람은 디바이스를 종료하고 재부팅하면 지워진다고 명시되어 있다.
(재부팅 시 지워지는 부분은 Manifest에 Boot receiver를 등록해 주어 해결)
이렇게 실행된 앱은 등록된 알람의 Receiver 메소드에서 CPU Wake Lock을 유지하고 지정해 둔 작업이 처리되기 전까지 Device를 켜둔다.
이렇듯 알람매니저는 해당 앱이 현재 실행되지 않더라도 특정 시간에 특정 작업(Notification, Sound 등등)을 실행하기 위해 사용한다.
특징
- 지정된 시간 혹은 간격으로 Intent를 실행
- Broadcast Receivcer와 조합해서 서비스를 시작하고 다른 작업을 시작 가능
- 앱이 실행 중이 아니거나 기기가 대기 상태인 경우에도 이벤트나 작업을 트리거
- Timer 혹은 지속적으로 실행하는 백그라운드 서비스를 사용하지 않고 작업을 예약
앱의 리소스 요구사항을 최소화하는데 도움 - 앱이 실행되고 있는 경우에는 AlarmManager보다는 Timer나 Thread & Handler 클래스 사용하는 것이 시스템 리소스를 더 효과적으로 제어할 수 있음
- 반복된 알람에 좋은 선택일 수 있지만 대상이 네트워크 작업이라면 배터리 소모가 커지고 서버에도 부담을 줄 수 있음
val alarmManager = getSystemService(Context.ALARM_SERVICE) as AlarmManager
알람 유형
ELAPSED_REALTIME | 시스템 부팅 시간 이후 (SystemClock.elapsedRealtime) 기준 |
ELAPSED_REALTIME_WAKEUP | + 기기 절전 모드 해제 |
RTC | UTC 시계 시간 (System.currentTimeMillis)를 기준 |
RTC_WAKEUP | + 디바이스가 꺼져있을 때 깨움 |
단일 알람 함수
set
- API 19 이후, 정확하지 않게(inexact) 동작
- 연기되고 시간이 지난 이후에 전달. OS에서 전체 시스템 알람을 "batch"하는 정책을 사용
- 디바이스가 Wake up 하는 것을 최소화하고 배터리 사용량을 최적화
- 실제 알람의 순서도 불일치
- 정확한 순서가 필요할 시, setWindow, setExact
setExact
- OS가 전달 시간 최적화하지 않음
- 요청된 시간에서 가능한 한 가까운 시간 내에 전달
setAndAllowWhileIdle
- Doze 모드일 때도 실행되는 set → batch 정책 사용
- 실제 Doze 모드일 때 실행 되어야 하는 알람의 경우에만 다뤄야 한다.
- ex) 일정이 되었다는 알림이 오는 캘린더
- Doze 모드일 때 동작해야 하므로, 과도한 사용을 막았다.
- 특정 앱의 빈도수 제한이 있음 (재사용 시 시간제한?)
setExactAndAllowWhileIdle
- setExact + setAndAllowWhileIdle
Version별 사용
when {
Build.VERSION.SDK_INT >= Build.VERSION_CODES.M ->
alarmManager.setExactAndAllowWhileIdle(
AlarmManager.RTC_WAKEUP, AlarmManager.INTERVAL_FIFTEEN_MINUTES, pendingIntent
)
Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT ->
alarmManager.setExact(
AlarmManager.RTC_WAKEUP, AlarmManager.INTERVAL_HOUR, pendingIntent
)
else ->
alarmManager.set(
AlarmManager.RTC_WAKEUP, AlarmManager.INTERVAL_DAY, pendingIntent
)
}
Doze 모드에서도 동작하려면 setAndAllowWhileIdle, setExactAndAllowWhileIdle를 사용
정확한 알람을 구현하려 할 때 Doze 모드에서도 발생할 수 있는 setExactAndAllowWhileIdle을 사용하는데 이상한 점이 있다. 문서에서도 정확한 알람과 Doze 모드에서도 발생하게 하려면 이 메서드를 사용하라 나왔는데 확인을 해보면 몇 분씩 늦어 정확한 시간에 수행하지 않을 때가 있다.
setExactAndAllowWhileIdle은 오해의 소지가 있다고 한다.
Android 문서에 따르면 이 메서드는 "정확히 명시된 시간에 전달되도록"이라 하고 나중에는 "요청된 트리거 시간에 최대한 가깝게"라고 설명하고 있다.
제한됨: 잠자기/대기 문서에 따르면 이 유형의 알람은 동일한 앱에서 9분당 두 번 이상 실행할 수 없다.
순서 미보장: OS는 동일한 앱에서도 다른 알람의 순서에 맞지 않게 이러한 종류의 알람이 트리거 되도록 일정을 변경할 수 있다.
유연성: Android는 이 방법을 사용하여 일반 알람보다 알람을 예약할 때 OS가 더 유연하도록 선택했다고 생각한다. 또한 "장치가 유휴 상태일 때 배터리 수명을 최적화하기 위해 일정을 더 자유롭게 지정할 수 있다."라고 명시되어 있다.
결국 OS 멋대로 실행해 주는 것이다.
알람 앱처럼 완벽하게 엄격한 알람이 필요한 경우는 setAlarmClock을 사용할 것
setAlarmClock
- AlarmClockInfo로 표현되는 시간에 알람을 예약
- 알람이 실행될 때, 디바이스를 깨우고, 사용자에게 알림을 주는 용도로 사용
화면 키기, 소리를 재생, 진동 등등
val calendar = Calendar.getInstance().apply {
timeInMillis = System.currentTimeMillis()
add(Calendar.HOUR_OF_DAY, hour)
add(Calendar.MINUTE, minute)
add(Calendar.SECOND, second)
}
val alarmClock = AlarmManager.AlarmClockInfo(calendar.timeInMillis, pendingIntent)
alarmManager.setAlarmClock(alarmClock, pendingIntent)
AlarmClockInfo에 들어가는 PendingIntent는 setAlarmClock을 이용해 알람을 지정하고
Notification으로 설정된 알람을 보여줄 때 알림 창에서 Notification을 탭 하면 실행되는 PendingIntent라고 한다.
반복 알람 함수
setIneactRepeating
- 반복 실행 알람 예약
- 시스템에서 batch
setRepeating
- 반복 실행 알람 예약
- 알람 지연 시 가능한 한 빨리 스킵된 알람이 전달됨
- 전달이 늦어져도 스케쥴은 정상 작동
- 한 시간마다 repeating, 7:45부터 8:45까지 폰이 자고 있다면 (8시 발생 알람 스킵)
8시 45분에 폰을 킬 때, 8시 발생 알람이 울리고 다음 알람은 9시에 잡힌다. (9시 45분이 아님)
⇒ 실제 발생 시간 기준 몇 시간 후 반복되는 알람을 하고 싶다면 일회성 알람을 사용해 자체 스케줄 할 것 - API 19 이후, 정확하지 않게 동작. 연기되고 시간이 지난 이후에 전달됨
알람 취소
cancel
Intent.filterEquals에 해당하는 알람이 취소
- Pending Intent 생성 시 등록한 RequestCode가 같아야 한다
- Extras를 제외 Action, Categories, Data, Mimetype, Package, Component가 같아야 한다
Doze 모드
Android 6.0(API 레벨 23)부터 추가된 기능으로 기기를 오랫동안 사용하지 않는 경우 앱의 백그라운드 CPU 및 네트워크 활동을 지연시켜 배터리 소모를 줄여주는 모드
사용자가 전원을 충전하지 않고 화면이 꺼진 채로 기기를 일정 기간 정지 상태로 두면 기기는 Doze 모드를 시작
기기를 움직이거나 화면 켜기, 충전기 연결 등을 하여 기기 절전 모드를 해제 시 시스템은 Doze 모드를 종료하고 모든 앱은 정상적인 활동으로 돌아간다.
알람 제약 사항
전체코드
https://github.com/wo9374/StudyProject/tree/main/AlarmManager
AlarmManager
https://yellowc-137.tistory.com/23
https://greedy0110.tistory.com/69
https://velog.io/@thevlakk/Android-AlarmManager-파헤치기-1
https://velog.io/@thevlakk/Android-AlarmManager-등록-여부-확인-및-주의점-2-Feat.삽질
https://velog.io/@thevlakk/Android-그간-있었던-AlarmManager-이슈들3
AlarmClockInfo PendingIntent
https://stackoverflow.com/questions/34699662/how-does-alarmmanager-alarmclockinfos-pendingintent-work
Doze 모드
https://kimch3617.tistory.com/entry/Doze-모드란-Doze-모드-최적화
PendingIntent Flag
https://developer88.tistory.com/187
'Android > Reference' 카테고리의 다른 글
Service (0) | 2023.12.14 |
---|---|
Notification (0) | 2023.12.09 |
DataStore / Preference, Proto (0) | 2023.12.04 |
BottomSheet (Persistent, Modal) (0) | 2022.05.02 |
RecyclerView Item에 Animation 주기 (0) | 2022.04.18 |