[android] AlarmManager 이용하여 특정 시간에 알림 보내기

Pyo·2024년 9월 15일
0

안드로이드에서 알람을 보내는 방법으로는 FCM(Firebase Cloud Messaging), AlarmManager, WorkManager 사용등이 있다고 알고있다. 현재 진행중인 토이프로젝트에서는 Alarm Mananger를 사용하는것 이 맞다고 판단하여 Alarm Manager를 사용하여 알림을 보내는 방법을 정리해보려고 한다.

AlarmManager 특징

  • 정확한 시간에 인텐트 실행: AlarmManager는 지정된 시간이나 간격에 인텐트를 실행할 수 있다. 이를 통해 특정 시간에 작업을 예약하거나 정기적으로 수행할 수 있다.

  • BroadcastReceiver와의 통합: BroadcastReceiver와 함께 사용하여 알람이 발생할 때 서비스를 시작하거나 다른 작업을 실행할 수 있다.

  • 애플리케이션 외부에서 작동: 앱이 실행 중이지 않거나 기기가 대기 상태일 때도 알람이 작동할 수 있다. 따라서 AlarmManager를 사용하면 앱이 백그라운드에 있거나 기기가 절전 모드일 때도 작업을 트리거할 수 있다.

  • 자원 효율성: 알람은 앱의 리소스 요구사항을 최소화하는 데 도움이 된다. 타이머나 지속적으로 실행하는 백그라운드 서비스 대신 알람을 사용하여 작업을 예약할 수 있다.

  • 앱이 실행 중인 경우: 앱이 실행 중일 때는 AlarmManager보다는 Timer나 Thread & Handler 클래스를 사용하는 것이 시스템 리소스를 보다 효율적으로 제어할 수 있다.

  • 반복 알람과 네트워크 작업: 반복적인 알람 설정에 유용하지만, 네트워크 작업이 포함된 경우 배터리 소모가 커지고 서버에 부담을 줄 수 있다. 네트워크 작업을 위한 경우, 호스팅 서버를 가지고 있다면 GCM (Google Cloud Messaging) 또는 Firebase Cloud Messaging을 사용하는 것이 더 나은 방법일 수 있다.

AlarmManager 사용해 보기

androidMainfest.xml - permission 추가


우선 permission 을 추가해 주어야 한다. 두 권한 모두 알림 권한을 위한 권한으로, POST_NOTIFICATIONS는 앱이 사용자에게 알림을 게시할 수 있도록 허용하는 권한, SCHEDULE_EXACT_ALARM은 앱이 정확한 시각에 알람을 설정할 수 있도록 허용하는 권한이다.

NotificationReceiver

NotificationReceiver는 안드로이드에서 AlramManger를 사용할때 BroadcaseReceiver를 상속받은 클래스를 활용해야 앱이 백그라운드 상태나 종료된 상태에서도 알림을 받을수 있다. onReceive() 함수는 BroadcaseReceiver가 Intent를 수신할 때 호출된다. 버전별 권한 체크후, NotificationCompat.Builder 클래스를통해 알림을 사용하여 앱에서 사용자에게 알림을 보낸다.

  
class NotificationReceiver : BroadcastReceiver() {
    override fun onReceive(context: Context?, intent: Intent?) {
        context?.let {
            // Android 13 이상에서는 POST_NOTIFICATIONS 권한이 필요
            if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.TIRAMISU &&
                context.checkSelfPermission(Manifest.permission.POST_NOTIFICATIONS) != PackageManager.PERMISSION_GRANTED) {
                // 권한이 없으면 알림을 표시하지 않음
                return
            }

            // 알림 생성
            val notification = NotificationCompat.Builder(it, "channel_id")
                .setSmallIcon(R.drawable.ic_launcher_foreground)
                .setContentTitle("로컬 알림")
                .setContentText("이것은 예약된 시간에 나타나는 알림입니다.")
                .setPriority(NotificationCompat.PRIORITY_HIGH)
                .build()

            // 알림 표시
            with(NotificationManagerCompat.from(it)) {
                notify(1, notification)
            }
        }
    }
}

androidMainfest.xml에 생성한 notificationReceiver 등록

AlarmManager 의 사용 및 전체 코드

우선 AlarmManager를 사용하여 알림을 보내기 위해서는 notificationChannel을 생성해 주어야 한다. createNotificationChannel() 함수를 통해 버전 체크후 notificationChannel를 생성한후, 버전 체크 및 알람 권한 설정에 문제가 없다면 알람이 울릴 시간을 설정한후, scheduleNotification() 함수를 통하여 위에서 생성한 NotificationReceiver를 호출하기 위한 인텐트를 생성후, 인텐트와 저장한 시간을 통해 AlarmManager의 시스템 서비스로 지정된 시간에 알림을 울릴수 있도록 한다.

class MainActivity : AppCompatActivity() {

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContentView(R.layout.activity_main)

        createNotificationChannel()

        // Android 12 이상에서 SCHEDULE_EXACT_ALARM 권한 확인
        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.S && !isExactAlarmPermissionGranted()) {
            Toast.makeText(this, "알람 권한을 설정하세요.", Toast.LENGTH_LONG).show()
            // 사용자를 설정 화면으로 안내하여 권한을 요청
            startActivity(Intent(Settings.ACTION_REQUEST_SCHEDULE_EXACT_ALARM))
        } else {

            
            val calendar = Calendar.getInstance().apply {
                add(Calendar.SECOND, 10) // 10초 후로 설정
				// 특정 시간 12:30분 알람 사용시
                // set(Calendar.HOUR_OF_DAY, 12)
                // set(Calendar.MINUTE, 30)
                // set(Calendar.SECOND, 0)
            }

            scheduleNotification(calendar.timeInMillis)
        }
    }

    private fun createNotificationChannel() {
        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
            val name = "LocalNotificationChannel"
            val descriptionText = "This is a channel for local notifications"
            val importance = NotificationManager.IMPORTANCE_HIGH
            val channel = NotificationChannel("channel_id", name, importance).apply {
                description = descriptionText
            }

            val notificationManager: NotificationManager =
                getSystemService(Context.NOTIFICATION_SERVICE) as NotificationManager
            notificationManager.createNotificationChannel(channel)
        }
    }

    private fun scheduleNotification(timeInMillis: Long) {
        val intent = Intent(this, NotificationReceiver::class.java)
        val pendingIntent = PendingIntent.getBroadcast(
            this,
            0,
            intent,
            PendingIntent.FLAG_UPDATE_CURRENT or PendingIntent.FLAG_IMMUTABLE
        )

        val alarmManager = getSystemService(Context.ALARM_SERVICE) as AlarmManager
        alarmManager.setExact(AlarmManager.RTC_WAKEUP, timeInMillis, pendingIntent)
    }

    // Android 12 이상에서 정확한 알람 권한이 부여되었는지 확인
    private fun isExactAlarmPermissionGranted(): Boolean {
        return if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.S) {
            val alarmManager = getSystemService(Context.ALARM_SERVICE) as AlarmManager
            alarmManager.canScheduleExactAlarms()
        } else {
            true
        }
    }
}

실행 화면

백그라운드 상태에서도 AlarmManager가 제대로 작동한다.

이렇게 AlarmManager를 이용하여 특정 시간에 앱에서 알림을 보내는 방법에 대해 알아보았다. 끝 !

1개의 댓글

comment-user-thumbnail
2024년 11월 2일

늘 잘보고 갑니다 ~
노력하시는 모습이 보기 좋네요~

답글 달기

관련 채용 정보