[Android] 그간 있었던 AlarmManager 이슈들(3)

양현진·2022년 12월 1일
1

Oh My Android

목록 보기
20/22
post-custom-banner

4월부터 접했던 AlarmManager가 드디어 어제(11/30)부로 감을 잡아 마지막 글을 쓰게 되었고, 그간 있었던 이슈들을 하나 씩 나열하여 이 글을 보는 누군가에겐 도움이 됐으면 하는 마음, 추후에 또 사용할 수 있는 나를 위해 포스팅을 한다.

블로그를 작성하면서 느낀건데, 나를 포함한 다른 AOS개발자들도 이 AlarmManager에 대해 골머리를 썩는듯하다.
아마 시스템을 통한 코드 작업인데, 다른 시스템 클래스와는 다르게 매우 복잡하게 얽혀있고 디버깅또한 알람 특성때문에 복잡하고 확인하기까지 느리기에 그런듯 하다.

[Android] AlarmManager 파헤치기 (1)
[Android] AlarmManager 등록 여부 확인 및 주의점 (2) Feat.삽질

저 중 1개를 보고 메일을 주셨던 분도 계셨다. 관련 이슈에 대한 메일이었는데, 매우 간절함이 보여 같은 개발자로서 고통을 느꼈다...

아무튼 그간 있었던 이슈를 나열해보자

1. (근본) 왜 알람이 안 울리지?

상황: AlarmManager라는 클래스를 알고서 코드를 따라치고 Run을 눌렀다. 테스트를 위한 것이니 2분 뒤로 잡아놨었다.
오, 알람이 울린다.! 나는 매일 3번씩 입력한 시각에 알람이 울리는 코드를 작성했으니 이제 실사용을 해보기로 했다.

.. 해당 시각이 되고 휴대폰을 하염없이 쳐다봤지만 알람은 오지 않았다. 그래서 다시 테스트를 위해 5분 뒤로 설정하고 실행했더니 이건 또 5분뒤에 오더라. 하지만 다시 실테스트를 들어가니 아침, 저녁, 밤에 설정한 알람은 오지 않았다.
머리가 터질듯한 혼란을 겪으며 거의 한달의 시간을 보냈다.

해결: [Android] AlarmManager 등록 여부 확인 및 주의점 (2) Feat.삽질에 해당 이슈와 해결법을 적었는데 지금보니 근본적인 방법은 아닌것 같다.
아마 AlarmManager가 debug모드(Run)에선 제대로 동작하지 않는듯 하다. 물론 모드가 문제인지 내가 디버그모드에서 뭘 수행했는지에 따라 달라질 수 있다는 생각은 하지만 몇달의 데이터를 토대로 실테스트를 하려면 signing apk로 release모드에서 수행하는 것을 권장한다.

2. 알람이 등록한 것 중 제일 나중에 것만 와요 or 알람이 등록한거 외에도 와요

상황: 위와 같이 아침, 저녁, 밤 3개의 알람을 등록한다. 근데 이상하게 제일 나중에 등록한 밤 알람만 온다
등록한거 이외에도 오는 알람은 다른 개발자분이 메일로 주신 상황이지만 아마 둘 다 PendingIntent와 관련된 이슈일 거라 생각한다

해결: 우선 AlarmManager를 잘 사용하기 위해선 PendingIntent에 대해 깊이 알아볼 필요가 있다.
기본적으로 Intent는 Android의 Component사이 통신 역할을 수행하는 "카톡"같은것 이라 생각할 수 있다. 정확히는 "카톡을 통해 보내는 데이터 형식"이 더 가깝다. Intent는 하나의 앱안에서 컴포넌트끼리 통신 매개채로 역할을 수행하는데, 만일 다른 프로세스(앱고 같은)곳에서도 통신을 원하는 경우에 Intent가 아닌 Pendingtent 객체로 통신할 수 있다.

이를 통해 앱과 시스템(알람 매니저)끼리 통신할 때도 PendingIntent를 사용한다. 여기서 시스템은 PendingIntent객체를 최대한 적게 활용하고 싶어한다. 그래서 PendingIntent에 다양한 Flag들이 존재하는데, Flag들의 하고싶어 하는 일을 공통적으로 살펴보면 기존 PendingIntent를 재사용 할 건지, 아니면 새로운 객체로 만들건지에 중점을 두고 있다. Android 12에서 업데이트된 FLAG_MUTTABLE, IMMUTABLE또한 재사용에 초점을 두고 있어 보인다.

존재하는 PendingIntent를 재사용하는것이 메모리적으로 더 효율적이기에 수정이 될 수 있는 경우가 다분한데, 여기서 위 이슈가 나온다. 알람 1 2 3 이 있으면 재사용 목적으로 1이 사용하던 것을 2가, 2가 사용하는 것을 3이 사용하여 결국 마지막에 등록한 3만이 알람을 울리게 할 수 있다.

그럼 각각 3개를 따로 생성해야 하느냐? 그건 아니다. getComponet 메서드에 requestCode라는 Int형 인자가 있는데 기본적으로 이를 각각 다르게 하면 가능하다.

보통 Alarm Manager는 Notification이랑 혼용해서 사용하는데 여기서 주의해야 한다.
혹여나 AlarmManager의 requestCode를 다르게하고 Notification의 requestCode는 같게 한다면 결국 나중의 것만 알림(노티)가 되기에 잘 구분해야 한다.

알람이 등록한거 외에도 오는 경우는
1. 본인도 모르게 다른 알람을 등록한 경우
2. 테스트를 진행하면서 전 알람을 지우지 않은 경우
가 있다.

알람을 취소하는 방법은 AlarmManager의 cancel메서드를 이용하면 되는데, 여기서 만약 requestCode가 다르거나 filterEquals가 false가 뜨게되면 전 알람은 지워지지 않는다. filterEqauls가 true가 나오는 경우는 action, category, data, type이 같은 경우이다. extraData는 포함되지 않는다고 한다.

3. 알람이 오는데 extra data가 null이 떠요

상황: Crashlytics을 보고있는데 AlarmReceiver쪽에서 NPE가 떴다.
엥? 나는 알람을 잘 받고 있는데? 다른 사용자에게서 뜨네?

버전차이인지 기종차이인지 도통 알 순 없지만 아마 이 때문에도 알람이 안나온다고 느낄수 있을것 같다.
검색을 해보니 단순 stackoverflow에서도 여러가지 답변이 있었다.
https://stackoverflow.com/questions/13596422/android-notification-pendingintent-extras-null

해결: 하지만 나는 전부 적용된 상태였고 extra data에 enum type을 serializable상태로 받고 있었는데
혹시 여기서 문제가 되나 싶어 int형태로 변경했더니 crash가 사라졌다.

4. setExactAndAllowWhileIdle을 써도 제 시각에 안와요

상황: 대부분 포스팅에서 정확한 알람을 구현하려 할때 도즈 모드에서도 발생할 수 있는 setExactAndAllowWhileIdle을 사용한다. 문서에서도 정확한 알람과 도즈 모드에서도 발생하게 하려면 이 메서드를 사용하라 나왔는데 왜 가끔 몇분씩 늦지?

setExactAndAllowWhileIdle은 오해의 소지가 있다한다. Android 문서에 따르면 이 메서드는 "정확히 명시된 시간에 전달되도록" 이라 하고 나중에는 "요청된 트리거 시간에 최대한 가깝게"라고 설명한다.

제한됨: 잠자기/대기 문서에 따르면 이 유형의 알람은 동일한 앱에서 9분당 두 번 이상 실행할 수 없습니다.
순서 미보장: OS는 동일한 앱에서도 다른 알람의 순서에 맞지 않게 이러한 종류의 알람이 트리거되도록 일정을 변경할 수 있습니다.
유연성: . Android는 이 방법을 사용하여 일반 알람보다 알람을 예약할 때 OS가 더 유연하도록 선택했다고 생각합니다. 또한 "장치가 유휴 상태일 때 배터리 수명을 최적화하기 위해 일정을 더 자유롭게 지정할 수 있습니다."라고 명시되어 있습니다.

결국 OS 멋대로라는 뜻이다.

해결: 그리하여 setAlarmClock이라는 함수를 사용하면 된다. 근데 이게 사용하는 사람이 없어서 그런지 검색해도 잘 안나온다.

val alarmClock = AlarmManager.AlarmClockInfo(alarmDate.timeInMillis, getPendingIntent(alarmType))
alarmManager.setAlarmClock(alarmClock, getPendingIntent(alarmType))

궁금했던 부분이 추가적으로 PendingIntent가 또 들어가는데 저건 어디에 사용되는지를 도통 모르겠으나 설명하는 글이 없어 패스한다.
문서에는 이렇게 설명되어 있다

예약한 애플리케이션에서 알람 시계의 세부정보를 표시하거나 편집하는 데 사용할 수 있는 인텐트를 반환합니다.

setAlarmClock으로 등록을 하게 되면 adb엔 아래와 같이 나오는데, 특이점은 Next wake from idle이라는 설명이 추가되었다.
setExactAndAllowWhileIdle은 등록해도 저런 메세지가 안나오는데 뭔가 더 믿음직하다. 실제로 여태까지 제 시각이 되자마자 알람이 울리고 있다.

alarmamager의 adb 설명은 위 링크 2번에 있으니 참고

5. 반복적인 알람을 하고 싶은데 정확한 알람을 사용하려면setExactAndAllowWhileIdle나 setAlarmClock을 써야하는데 얘낸 setRepeat같은 기능이 없어요

이건 그냥 넣은건데 BroadCast의 onReceive에서 action이나 extra로 알람을 분기하고 다시 날짜를 하루 더해서 재등록하면 된다.
나는 날짜 함수로 Calendar 클래스를 애용하기에

Calenader.getInstance().apply {
	// 해당 알람 시각 넣기
    // 예
    // 24시간 기준
    set(Calendar.HOUR_OF_DAY , 20)
    // 하루 추가
	add(Calendar.DAY_OF_MONTH, 1)
}

아, 재부팅될 때 등록한 알람이 사라지니 이 또한 재등록이 필요.

profile
Android Developer
post-custom-banner

1개의 댓글

comment-user-thumbnail
2022년 12월 8일

댓글 왜 지워요. 주인장 나와

답글 달기