[Android,Kotlin] BroadcastReceiver + NotificationManager로 특정 시간에 메시지 보내기

 

Gradle 추가

implementation "androidx.core:core-ktx:1.7.0"

core-ktx는 개발 중에 추가하는 경우가 많으므로 중복추가 되지 않도록 확인

 

Manifest 설정

Manifest에 Receiver를 등록해야 한다.

안드로이든느 부팅이 끝나면 BOOT_COMPLETED라는 intent를 브로드캐스트 한다.

PendingIntent를 통해서 Notification을 통해서 사용자에게 메시지를 전달하게 되는데,  재부팅 시 해당 설정이 사라진다.

재부팅 후에도 PendingIntent가 남아있으려면 Manifest에 등록이 필요하다.

 

<receiver android:name="com.jinuemong.SwallowMonthJM.AlarmBroadCastReceiver"
    android:enabled="true"
    android:exported="false">
    <intent-filter>
        <action android:name="android.intent.action.BOOT_COMPLETED" />
    </intent-filter>
</receiver>

application 설정 내부에 위와같이 BroadCastReceiver를 선언한다.

AlarmBroadCastReceiver는 내가 선언한 BroadCastReceiver로 아래와 같이 클래스를 선언한다.

 

BroadCastReceiver 선언

class AlarmBroadCastReceiver : BroadcastReceiver()

해당 클래스는 반드시 onReceive 함수를 오버라이드 해야한다.

onReceive 함수 내부에서 알림을 보내기 위한 NotificationManger를 선언하고 실행한다.

 

class AlarmBroadCastReceiver : BroadcastReceiver() {

    @SuppressLint("UnsafeProtectedBroadcastReceiver", "CommitPrefEdits")
    @RequiresApi(Build.VERSION_CODES.O)
    override fun onReceive(p0: Context?, p1: Intent?) {
        // 알람을 받는 곳
        if (p0!=null && p1!=null) {
            when(p1.extras?.get("code")){
                MainActivity.REQUEST_CODE_1->{ // 일 리셋
                    Toast.makeText(p0, "init current Data", Toast.LENGTH_SHORT).show()
                    Log.d("initCurrentData", LocalDateTime.now().toString())

                    val code = MainActivity.REQUEST_CODE_1

                    // 인텐트 생성
                    val intent = Intent(p0, LoginActivity::class.java)
                    val pendingIntent = PendingIntent.getActivity(p0, 0, intent, PendingIntent.FLAG_IMMUTABLE)
                    // 알림 생성 - 메시지 전달
                    val builder = NotificationCompat.Builder(p0, "my_channel")
                        .setSmallIcon(android.R.drawable.ic_dialog_info)
                        .setContentTitle("[Swallow Notification]")
                        .setContentText("update data today!")
                        .setContentIntent(pendingIntent)
                        .setAutoCancel(true)
                    // 알림 실행
                    NotificationManagerCompat.from(p0).apply {
                        notify(code, builder.build())
                    }
                    //채널 등록
                    if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
                        val channel = NotificationChannel(
                            "my_channel", "Notification",
                            NotificationManager.IMPORTANCE_DEFAULT
                        ).apply {
                            description = "Notification"
                        }
                        val notificationManager =
                            p0.getSystemService(Context.NOTIFICATION_SERVICE) as NotificationManager
                        notificationManager.createNotificationChannel(channel)
                    }
                }
                MainActivity.REQUEST_CODE_2->{ //월 리셋
                    Toast.makeText(p0, "initMonthData", Toast.LENGTH_SHORT).show()
                    Log.d("initMonthData", LocalDateTime.now().toString())
                    val code = MainActivity.REQUEST_CODE_2
                    // 인텐트 생성
                    val intent = Intent(p0, LoginActivity::class.java)
                    val sr = p0.getSharedPreferences("fragment",Context.MODE_PRIVATE)
                    sr.edit().putString("fragment","recently").apply()
                    val pendingIntent = PendingIntent.getActivity(p0, 0, intent, PendingIntent.FLAG_IMMUTABLE)

                    // 알림 생성 - 메시지 전달
                    val builder = NotificationCompat.Builder(p0, "my_channel_2")
                        .setSmallIcon(android.R.drawable.ic_dialog_info)
                        .setContentTitle("[Swallow Notification_2]")
                        .setContentText("update month data!")
                        .setContentIntent(pendingIntent)
                        .setAutoCancel(true) //클릭 시 삭제
                    // 알림 실행
                    NotificationManagerCompat.from(p0).apply {
                        notify(code, builder.build())
                    }
                    if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
                        val channel = NotificationChannel(
                            "my_channel_2", "Notification_2",
                            NotificationManager.IMPORTANCE_DEFAULT // 알림음 있음
                        ).apply {
                            description = "Notification_2"
                        }
                        val notificationManager =
                            p0.getSystemService(Context.NOTIFICATION_SERVICE) as NotificationManager
                        notificationManager.createNotificationChannel(channel)
                    }
                }

            }
        }
    }

}

 

onReceive에서는 context와 Intent를 전달 받는다.

context를 통해서 리시버를 실행시킨 곳에서  extras 데이터를 전달받을 수 있으며, 이를 통해서 코드를 구분한다.

전달 받은 코드가 REQUEST_CODE_1인 경우에는 day Data를 리셋하는 메시지를 전달,

전달 받은 코드가 REQUEST_CODE_2인 경우에는 month Data를 리셋하는 메시지를 전달한다.

 

https://jinudmjournal.tistory.com/97

 

[Django, Android] WorkManger로 매일 특정 시간에 백그라운드 작업 실행

WorkManager 백그라운드 작업에는 AlramManager, JobScheduler, JobDispatcher, WorkManager 등을 사용한다. 같은 시간에 동일한 작업을 진행하려면 AlramManager를 주로 사용하는데, 정확한 시간을 설정하는 것이 어려

jinudmjournal.tistory.com

 

이전에 작성한 포스팅인 WorkManager에서 아래와 같은 코드가 있었다.

 

            // 실행
            if (Calendar.getInstance().get(Calendar.DAY_OF_MONTH)==1){
                applicationContext.sendBroadcast(monthBroadIntent())
            }else{
                applicationContext.sendBroadcast(dayBroadIntent())
            }

 

이는 앱의 day or month 데이터가 갱신되었다는 메시지를 전달하는 코드이다.

아래의 코드로 동작하며 상황에 따라 다른 REQUEST_CODE를 전달받는다.

//앱 실행 알림
private fun dayBroadIntent() :Intent {
    return Intent(applicationContext,AlarmBroadCastReceiver::class.java)
        .let {intent->
            intent.putExtra("code", REQUEST_CODE_1)
        }
}

private fun monthBroadIntent() : Intent{
     return Intent(applicationContext,AlarmBroadCastReceiver::class.java)
        .let { intent ->
            intent.putExtra("code", REQUEST_CODE_2)
        }
}

 

이를 통해서 AlarmBroadCastReceiver의  onReceive() 함수가 실행되는 것이다.

 

첫번째 인자를 받는 경우로 코드를 살펴보면,

 

1. 인텐트를 생성

 

늦은 시작 Intent인 pendingIntent를 선언한다.

// 인텐트 생성
val intent = Intent(p0, LoginActivity::class.java)
val pendingIntent = PendingIntent.getActivity(p0, 0, intent, PendingIntent.FLAG_IMMUTABLE)

 

2. 알림 생성

 

사용자에게 보낼 알림을 생성한다.

메시지의 내부를 담당하는 builder를 선언하며, 아이콘, 제목, 메시지 내용을 설정해준다.

이 후에 pendingIntent를 실행 인텐트로 넣어주고, AutoCancel를 통해서 클릭 시 삭제가 가능하게 한다.

// 알림 생성 - 메시지 전달
val builder = NotificationCompat.Builder(p0, "my_channel")
    .setSmallIcon(android.R.drawable.ic_dialog_info)
    .setContentTitle("[Swallow Notification]")
    .setContentText("update data today!")
    .setContentIntent(pendingIntent)
    .setAutoCancel(true)

 

3. 알림 실행 

 

생성한 알림을 실행한다.

// 알림 실행
NotificationManagerCompat.from(p0).apply {
    notify(code, builder.build())
}

 

4. 채널 등록 

 

NotificationChannel을 통해서 channel을 등록한다.

SDK 버전을 검사해서 Notification 기능이 동작할 수 있는지 확인하고,

NotificationManager를 통해서 생성한 알림을 등록한다.

//채널 등록
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
    val channel = NotificationChannel(
        "my_channel", "Notification",
        NotificationManager.IMPORTANCE_DEFAULT
    ).apply {
        description = "Notification"
    }
    val notificationManager =
        p0.getSystemService(Context.NOTIFICATION_SERVICE) as NotificationManager
    notificationManager.createNotificationChannel(channel)
}

 

 

 

원하는 기능인 매일 자정에 새로운 기능이 업데이트 되었다는 알림을 받을 수 있었다.