본문 바로가기

안드로이드/코틀린

[안드로이드/Android] OS 버전별 알람 구현 방법과 반복 알람 설정까지

반응형

수정이력

- 2020. 3. 18 : 최초 작성

- 2021. 3. 10 : 알람 삭제 추가 

 

 

 

알람과 같이 일정 시간이 지난 후 특정 기능을 수행하는 작업을 할때 알람 매니저를 사용하곤 합니다. 사용법은 어렵지 않지만 sdk 버전별 파편화가 존재하고 메소드가 많아서 사용할때 마다 어려움이 있었습니다. 그리고 버전뿐만 아니라 경우에 따라 고려해야 합니다.

  • 어떤 시간을 기준으로 할것인지
  • 도즈모드에 진입한 디바이스에대한 알림 유무
  • 알람을 반복적으로 수행할 것 인지
  • 정확한 시간에 수행할 것인지에 따라도 조금씩 달라집니다.

 

setExactAndAllowWhileIdle

마시멜로(6.0 / api23) 버전부터 도즈모드가 도입되면서 기존에 사용하던 setExact, set 메소드를 사용했을 경우 도즈모드에 진입한 경우 알람이 울리지 않습니다.  'setExactAndAllowWhileIdle'은 도즈모드에서도 잠깐 깨어나 알람을 울리게 합니다.

해당 메소드를 사용할 경우  도즈모드일 경우를 9분이내에 한번밖에 작동하지 않습니다. 일반적으론 1분이여도 알림이 정상적으로 작동합니다.

setExactAndAllowWhileIdle(int type, long triggerAtMillis, PendingIntent operation)

type : 알람의 타입으로 4가지가 있습니다.

  • RTC_WAKEUP : 2번째 인자로 넘겨준 시간에 맞게 알림이 울리며 잠들어 있는 기기도 깨웁니다.
  • RTC : RTC_WAKEUP 과 유사하지만 해당 타입은 잠들어 있는 기기는 깨우지 않습니다.
  • ELAPSED_REALTIME_WAKEUP : 기기가 부팅된 시점으로부터 2번째 인자로 받은 시간이 경과된 후에 알람이 울리며 잠들어 있는 기기도 깨웁니다.
  • ELAPSED_REALTIME : ELAPSED_REALTIME_WAKEUP과 유사하지만 잠들어 있는 기기는 깨우지 않습니다.

triggerAtMillis : 타입에 따른 시간입니다.  

  • RTC, RTC_WAKEUP : 시간을 밀리세컨드 단위로 바꾼 값을 넣어줍니다. 저는 그래서 주로 캘린더를 사용해 가져옵니다. 예시는 아래를 참고해주세요. 캘린더가 아닌 'System.currentTimeMillis()' 를 사용하면 현재 시간을 밀리세컨드로 반환 받아, 원하는 시간을 더한 값을 입력하면 더한 시간 뒤에 알림이 울립니다.
  • ELAPSED_REALTIME, ELAPSED_REALTIME_WAKEUP : 부팅된 이후의 시간 값으로 마찬가지로 밀리세컨드 단위기 때문에 1000이 1초입니다. 'SystemClock.elapsedRealTime()'를 사용해서 부팅이후 경과된 시간을 반환 받고 해당 값에 원하는 시간을 더한 값을 입력하면 더한 시간 뒤에 알림이 울립니다.

pendingIntent : 알림이 울렸을때 수행한 작업을 명시하며 receiver를 사용합니다.

 

 

setExact

키켓(4.4 / api19) 버전부터 도입된 메소드로 보편적으로 사용됩니다. 정확한 시간에 알람을 보장하지만 중요한 알람에만 사용하라고 구글 문서에 기재되어 있습니다.

setExact (int type, long triggerAtMillis, PendingIntent operation)

set

킷켓이상 버전에서는 설정한 시간에 정확히 울리지 않기 때문에 키켓 미만 버전에서만 사용해야 합니다.

set (int type, long triggerAtMillis, PendingIntent operation)

 

반복 알림에 대해선 setInexactRepeating과 setRepeating을 사용했었는데 도즈모드에대한 예외처리가 불가능해 정확한 일을 수행하는 경우엔 적합하지 않습니다. 그래서 저는 위 3개를 사용해 알람 매니저가 리시버를 호출했을때 또 다시 알람을 등록해서 반복하는 알림을 사용했습니다. 하지만 킷켓이상 버전에선 알람이 정확한 시간에 울리지 않기 때문에 저와 같은 방법을 사용해야 합니다.

그리고 핸드폰이 꺼지면 등록된 알림이 모두 삭제되기 때문에 BootReceiver를 사용해서 다시 켜졌을때 알림을 다시 등록해야 합니다.

 

 

버전별 알람 등록 예제 소스 

시간을 입력받아서 알림을 울리는 코드로, 입력 받은 시간이 지난 시간이거나 지금과 같은 시간일 경우 'calendar.add(Calendar.DATE,1)을 통해 다음 날 울리게 합니다.

   fun setAlarm(hour: Int, context: Context) {
            val calendar = Calendar.getInstance()
            calendar.set(Calendar.HOUR_OF_DAY, hour)

            calendar.set(Calendar.MINUTE, 0)
            calendar.set(Calendar.SECOND, 0)

            var nowCalendar = Calendar.getInstance()
            if (calendar.before(nowCalendar) || nowCalendar.time == calendar.time) {
                //이미 지난 시간 일 경우
                calendar.add(Calendar.DATE, 1)
            }

            var intent = Intent(context, MealsAlarmReceiver::class.java)
            var pendingIntent = PendingIntent.getBroadcast(context, REQ_MEAL_ALARM, intent, PendingIntent.FLAG_UPDATE_CURRENT)

            val alarmManaer = context.getSystemService(Context.ALARM_SERVICE) as AlarmManager
            when {
                Build.VERSION.SDK_INT >= Build.VERSION_CODES.M -> alarmManaer.setExactAndAllowWhileIdle(AlarmManager.RTC_WAKEUP, calendar.timeInMillis, pendingIntent)
                Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT -> alarmManaer.setExact(AlarmManager.RTC_WAKEUP, calendar.timeInMillis, pendingIntent)
                else -> alarmManaer.set(AlarmManager.RTC_WAKEUP, calendar.timeInMillis, pendingIntent)
            }
        }

알람 삭제(2021년 3월 10일 추가)

fun remove(context: Context, alarmId: Long) {
            var intent = Intent(context, AlarmReceiver::class.java)
            var pendingIntent =
                PendingIntent.getBroadcast(context, alarmId, intent, PendingIntent.FLAG_NO_CREATE)
            val alarmManager = context.getSystemService(Context.ALARM_SERVICE) as AlarmManager
            alarmManager.cancel(pendingIntent)
        }

PendingIntent 생성시 FLAG_NO_CREATE를 사용하면 기존에 생성된 PendingIntent를 반환합니다.

 


개발하시면서 잘 안풀리시는 문제들이 생기실때 댓글을 남기시면 최대한 빨리 피드백을 드리겠습니다. 해결을 약속할순 없지만 혼자 생각하는것 보단 둘이 생각하는게 개발할때도 좋더라구요 ㅎㅎ

감사합니다!

반응형