경험의 기록

AlarmManager 를 사용하면 어플리케이션이 사용중이지 않을 때에도,

시간 기반 작업을 생성할 수 있다.

 

하지만 효율성의 문제로 여러가지 권장사항이 존재한다.

 

1️⃣ 네트워크 작업보다 로컬 작업을 위해 AlarmManager를 사용해야함

-> 네트워크 작업은 동기화 어댑터와 함께 서버에서 직접 사용하는 것이 훨씬 더 유연하고, 만약 정확한 시간에 호출되어 동기화하는 알람이라면 그 앱의 모든 유저들을 동기화 하게 되어 서버의 과부하가 발생한다.

 

2️⃣ 불필요하게 기기의 절전모드를 해제하면 안됨

-> 시스템의 리소스가 빨리 소모되어 배터리 효율이 저하함.

 

3️⃣ 알람 트리거를 필요한 수준보다 정밀하게 설정하면 안됨

-> 정밀하게 설정하는 setRepeating() 대신 setInexactRepeating() 을 사용하면 여러 앱의 반복 알람을 동기화하고 동시에 실행하기 때문에 시스템이 절전모드를 해제해야하는 횟수가 줄어들어 배터리 효율이 좋아지기 때문.

 

4️⃣ Real time보다 Elapsed time 사용

-> Real time(실제시간)은 UTC 시간을 사용하기 때문에 사용자가 설정한 시간대, 언어의 영향을 받아 오동작할 가능성이 있기 때문에 가능하다면 Elapsed time(기기 부팅 후 경과시간)을 사용해야함

 

 

 

https://developer.android.com/training/scheduling/alarms?hl=ko 

 

반복 알람 예약  |  Android 개발자  |  Android Developers

알람(AlarmManager 클래스 기반)을 사용하면 애플리케이션이 사용되지 않을 때 시간 기반 작업을 실행할 수 있습니다. 예를 들어, 알람을 사용하여 일기예보를 다운로드하는 것과 같이 하루에 한 번

developer.android.com

위 개발자 문서에서 더 자세한 내용을 확인할 수 있다.


사용해보기

1️⃣ 일회성 알람

 

BroadcastReceiver 추가

New -> Other -> BroadcastReceiver 를 클릭하여 추가해준다.

 

class MyReceiver : BroadcastReceiver() {

    lateinit var notificationManager: NotificationManager

    override fun onReceive(context: Context, intent: Intent) {
        notificationManager = context.getSystemService(
            Context.NOTIFICATION_SERVICE) as NotificationManager

        createNotificationChannel()
        deliverNotification(context)
    }

클래스에서 lateinit으로 notificationManager를 선언해주고

 

Broadcast가 수신되면 자동으로 호출되는 메서드인 onReceive 에서 초기화해준다.

 

또한 Notification 을 띄우기 위한 Channel 등록을 위한 createNotificationChannel 메소드와

Notification 등록을 위한 deliverNotification 메소드를 구현해준다.

 

상수 클래스

class Constant {
    companion object {
        // 아이디 선언
        const val NOTIFICATION_ID = 0
        const val CHANNEL_ID = "notification_channel"

        // 알림 시간 설정
        const val ALARM_TIMER = 5
    }
}

다른 클래스에서 사용할 상수들을

따로 정리하여 저장해놓았다.

 

createNotificationChannel 

오레오 버전 이상에서는

노티피케이션을 띄우기 위하여 의무적으로 채널을 등록해야 한다.

// Notification 을 띄우기 위한 Channel 등록
    fun createNotificationChannel(){
        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
            val notificationChannel = NotificationChannel(
                CHANNEL_ID, // 채널의 아이디
                "채널 이름입니다.", // 채널의 이름
                NotificationManager.IMPORTANCE_HIGH
                /*
                1. IMPORTANCE_HIGH = 알림음이 울리고 헤드업 알림으로 표시
                2. IMPORTANCE_DEFAULT = 알림음 울림
                3. IMPORTANCE_LOW = 알림음 없음
                4. IMPORTANCE_MIN = 알림음 없고 상태줄 표시 X
                 */
            )
            notificationChannel.enableLights(true) // 불빛
            notificationChannel.lightColor = Color.RED // 색상
            notificationChannel.enableVibration(true) // 진동 여부
            notificationChannel.description = "채널의 상세정보입니다." // 채널 정보
            notificationManager.createNotificationChannel(
                notificationChannel)
        }
    }

채널을 등록해주고

 

deliverNotification 

// Notification 등록
    private fun deliverNotification(context: Context){
        val contentIntent = Intent(context, MainActivity::class.java)
        val contentPendingIntent = PendingIntent.getActivity(
            context,
            NOTIFICATION_ID, // requestCode
            contentIntent, // 알림 클릭 시 이동할 인텐트
            PendingIntent.FLAG_UPDATE_CURRENT
        /*
        1. FLAG_UPDATE_CURRENT : 현재 PendingIntent를 유지하고, 대신 인텐트의 extra data는 새로 전달된 Intent로 교체
        2. FLAG_CANCEL_CURRENT : 현재 인텐트가 이미 등록되어있다면 삭제, 다시 등록
        3. FLAG_NO_CREATE : 이미 등록된 인텐트가 있다면, null
        4. FLAG_ONE_SHOT : 한번 사용되면, 그 다음에 다시 사용하지 않음
         */
        )

        val builder = NotificationCompat.Builder(context, CHANNEL_ID)
            .setSmallIcon(R.drawable.ic_alarm) // 아이콘
            .setContentTitle("타이틀 입니다.") // 제목
            .setContentText("내용 입니다.") // 내용
            .setContentIntent(contentPendingIntent)
            .setPriority(NotificationCompat.PRIORITY_HIGH)
            .setAutoCancel(true)
            .setDefaults(NotificationCompat.DEFAULT_ALL)

        notificationManager.notify(NOTIFICATION_ID, builder.build())
    }

노티피케이션을 등록해준다.

 

메인액티비티

class MainActivity : AppCompatActivity() {

    private lateinit var binding : ActivityMainBinding

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        // 뷰바인딩
        binding = ActivityMainBinding.inflate(layoutInflater)
        setContentView(binding.root)

        val alarmManager = getSystemService(ALARM_SERVICE) as AlarmManager

        val intent = Intent(this,MyReceiver::class.java)
        val pendingIntent = PendingIntent.getBroadcast(
            this, NOTIFICATION_ID, intent,
            PendingIntent.FLAG_UPDATE_CURRENT
        )

        // 토글버튼 활성화 시 알림을 생성하고 토스트 메세지로 출력
        binding.toggleButton.setOnCheckedChangeListener { _, check ->
            val toastMessage = if (check) {
                val triggerTime = (SystemClock.elapsedRealtime() // 기기가 부팅된 후 경과한 시간 사용
                        + ALARM_TIMER * 1000) // ms 이기 때문에 초단위로 변환 (*1000)
                alarmManager.set(
                    AlarmManager.ELAPSED_REALTIME_WAKEUP,
                    triggerTime,
                    pendingIntent
                ) // set : 일회성 알림
                "$ALARM_TIMER 초 후에 알림이 발생합니다."
            } else {
                alarmManager.cancel(pendingIntent)
                "알림 예약을 취소하였습니다."
            }

            /*
            1. ELAPSED_REALTIME : ELAPSED_REALTIME 사용. 절전모드에 있을 때는 알람을 발생시키지 않고 해제되면 발생시킴.
            2. ELAPSED_REALTIME_WAKEUP : ELAPSED_REALTIME 사용. 절전모드일 때도 알람을 발생시킴.
            3. RTC : Real Time Clock 사용. 절전모드일 때는 알람을 발생시키지 않음.
            4. RTC_WAKEUP : Real Time Clock 사용. 절전모드 일 때도 알람을 발생시킴.
             */

            Toast.makeText(this, toastMessage, Toast.LENGTH_SHORT).show()
        }
    }
}

 

토글버튼 활성화 시

정한 시간 뒤에 알림이 발생한다는 토스트가 뜨고,

 

알림이 발생한다.

 

노티피케이션의 속성으로 들어가면 세부내용을 볼 수 있다.

 

정확한 시간에 알람 발생 시키기

// 토글버튼 활성화 시 알림을 생성하고 토스트 메세지로 출력
        binding.toggleButton.setOnCheckedChangeListener { _, check ->
            val toastMessage = if (check) {
                val triggerTime = (SystemClock.elapsedRealtime() // 기기가 부팅된 후 경과한 시간 사용
                        + ALARM_TIMER * 1000) // ms 이기 때문에 초단위로 변환 (*1000)
                alarmManager.setExact(
                    AlarmManager.ELAPSED_REALTIME_WAKEUP,
                    triggerTime,
                    pendingIntent
                ) // set : 일회성 알림
                "$ALARM_TIMER 초 후에 알림이 발생합니다."
            } else {
                alarmManager.cancel(pendingIntent)
                "알림 예약을 취소하였습니다."
            }

set을 사용하면, 비교적 정확하지 않은 시간에 알람이 발생한다.

alarmManager에서

set이 아닌 setExact를 사용하면 정확한 시간에 알람이 발생한다.

 

절전모드 일 때 동작

  • setAndAllowWhileIdle : set()과 동일하지만 절전모드에서도 동작
  • setExactAndAllowWhileIdle : setExact()과 동일하지만 절전모드에서도 동작

2️⃣ 반복 알람

binding.toggleButton2.setOnCheckedChangeListener { _, check ->
            val toastMessage = if (check) {
                val repeatInterval = AlarmManager.INTERVAL_FIFTEEN_MINUTES
                /*
                1. INTERVAL_FIFTEEN_MINUTES : 15분
                2. INTERVAL_HALF_HOUR : 30분
                3. INTERVAL_HOUR : 1시간
                4. INTERVAL_HALF_DAY : 12시간
                5. INTERVAL_DAY : 1일
                 */
                val triggerTime = (SystemClock.elapsedRealtime()
                        + repeatInterval)
                alarmManager.setInexactRepeating(
                    AlarmManager.ELAPSED_REALTIME_WAKEUP,
                    triggerTime, repeatInterval,
                    pendingIntent
                ) // setInexactRepeating : 반복 알림
                "${repeatInterval/60000}분 마다 알림이 발생합니다."
            } else {
                alarmManager.cancel(pendingIntent)
                "알림 예약을 취소하였습니다."
            }
            Toast.makeText(this, toastMessage, Toast.LENGTH_SHORT).show()
        }

setInexactRepeating 를 사용하여

반복되는 알람을 설정할 수 있다.

하지만 반복주기는 정해진 5가지의 주기로밖에 설정할 수 없다.

 

원하는 시간주기에 알람 발생 시키기

binding.toggleButton2.setOnCheckedChangeListener { _, check ->
            val toastMessage = if (check) {
                val repeatInterval : Long = ALARM_TIMER * 1000L
                val triggerTime = (SystemClock.elapsedRealtime()
                        +  repeatInterval)
                alarmManager.setRepeating(
                    AlarmManager.ELAPSED_REALTIME_WAKEUP,
                    triggerTime, repeatInterval,
                    pendingIntent
                )
                "${repeatInterval/1000}초 마다 알림이 발생합니다."
            } else {
                alarmManager.cancel(pendingIntent)
                "알림 예약을 취소하였습니다."
            }
            Toast.makeText(this, toastMessage, Toast.LENGTH_SHORT).show()
        }

setRepeating 를 사용하면

주기도 맘대로 정할 수 있다.

 


3️⃣ Real Time 사용하기, 부팅

// realtime
        binding.toggleButton3.setOnCheckedChangeListener { _, check ->
            val toastMessage = if (check) {
                val repeatInterval : Long = ALARM_TIMER * 1000L
                val calendar = Calendar.getInstance().apply {
                    timeInMillis = System.currentTimeMillis()
                    set(Calendar.HOUR_OF_DAY,5)
                    set(Calendar.MINUTE,30)
                }
                alarmManager.setRepeating(
                    AlarmManager.RTC_WAKEUP,
                    calendar.timeInMillis,
                    repeatInterval,
                    pendingIntent)
                "알림이 발생합니다."
            } else {
                alarmManager.cancel(pendingIntent)
                "알림 예약을 취소하였습니다."
            }
            Toast.makeText(this, toastMessage, Toast.LENGTH_SHORT).show()
        }

원하는 날짜에도 알림을 발생시킬 수 있습니다.

 

기기가 다시 시작되면 알람 시작

class BootReceiver : BroadcastReceiver() {
    companion object {
        const val TAG = "BootReceiver"
    }

    override fun onReceive(context: Context, intent: Intent) {
        Log.d(TAG, "Received intent : $intent")
        if (intent.action == "android.intent.action.BOOT_COMPLETED") {
            // Register alarm
        }
    }
}

부트리시버를 만들어

부팅시 알림을 다시 호출해줍니다.

 

https://github.com/HanYeop/AndroidStudio-Practice2/tree/master/AlarmEx

 

HanYeop/AndroidStudio-Practice2

(2021.05.20~) 안드로이드 학습 내용 저장소. Contribute to HanYeop/AndroidStudio-Practice2 development by creating an account on GitHub.

github.com

 

 

참고

https://codechacha.com/ko/android-alarmmanager/

https://developer.android.com/training/scheduling/alarms?hl=ko 

 

 

 

반응형

공유하기

facebook twitter kakaoTalk kakaostory naver band
loading