모바일/Android

Notification과 Alarm

Patti Smith 2023. 11. 17.

Notification?

시스템 영역, 알림 영역에 표시하는 것을 알림이라고 한다. 어플 내부가 아닌 시스템 창(외부)에서 사용자가 알림을 표시하고 세부 내용을 표시한다. 어플의 실행 내부가 아니기 때문에 여태껏 배웠던 엑티비티와는 다른 매커니즘을 가질 수밖에 없다.

  • 앱 내부가 아닌 외부(알림 영역)에 사용자에게 표시할 수 있는 메시지
  • 아이콘 및 메시지를 사용하여 정보 표시
  • swipe로 내린 알림창을 통해 세부 정보 확인
  • 알림 영역은 알림 창은 시스템 제어 영역
  • 이외에도 헤드업 알림, 잠금 화면 알림, 앱 아이콘 배지 등으로 사용한다.

 

그렇다면 notification은 어떻게 만들까?

 

알림을 만들기 위해선 내용, 아이콘 등 알림 자체를 표현하는 정보(class)가 있어야 하며, 이렇게 만든 알림은 시스템을 통해 요청된다. 이 일련의 과정을 위해 다음과 같은 구성 요소를 가질 수 있다.

 

 

notification의 구성요소

NotificationManager

알림을 관리하는 시스템 제공 클래스다. 이 클래스를 getter로 받아온 뒤 만들어진 알람을 요청할 수 있다.

getSystemService(NOTIFICATON_SERVICE);

 

Notification

어떤 정보를 가지고 알림을 만들 건지 정의하는 알림 클래스다. 알림에 대한 UI 정보와 작업을 표현한다. 이 알림 클래스를 만들어 위의 시스템 클래스에 전달하면 된다.

하지만 알림을 위해 설정할 수 있는 정보들이 다양하다보니, 알림 클래스를 만들기 위해 Builder 디자인 패턴을 사용하여 알림 클래스를 만들 수 있다.

 

NotificationCompat.Builder

알림을 생성하는 내부 클래스다. 이전에는 Notification.Builder를 사용했다. 현재(API 26이상)는 추가된 여러 기능/호환성을 위해 NotificationCompat.Builder를 사용한다. 기본적으로 import되어 있기 때문에 따로 추가할 필요는 없다. 클래스 사이의 관계를 정의할 필요 없이 빌더를 통해(setter) 필요한 Notification을 만든 뒤 매니저 클래스에 전달하면 된다.

 

Notification Channel

이전과 달리 방송의 종류가 많아져 알림을 구분해야 할 필요가 생겼다. 중요도 순위에 따라 채널(묶음)을 만들어 알림을 등록하는 작업을 해야 한다. 안드로이드 8.0이상부터는 의무적으로 사용해야 한다.

 

알림을 만드는 방법을 알아보았으니 이제 알림을 어떻게 구현하고 사용해야 하는지 알아보자.

 

알림 구현 및 사용 절차

1. 권한 등록
안드로이드 API 33이상부터는 알림 기능을 받을 것인지 AndroidManifest에 퍼미션을 추가해야 한다. 단 이런 알림은 실시간으로 확인해야 해서 관련 코드까지 작성해야 한다.
<uses-permission android:name="android.permission.POST_NOTIFICATIONS"/>

 

2. NotificationChannel 생성
알림의 우선 순위 지정

 

3. NotificationCompat.Builder 생성 및 항목 설정
notification 객체를 생성 후 매니저에게 전달한다

 

4. 알림 표시
생성된 객체를 매니저에게 전달할 때 호환성을 위해 NotificationManager보다 NotificationManagerCompat을 사용하는 게 더 좋다.

 

5. 알림 수정 및 삭제

 

 

 


 

Notification의 사용?

알림 채널의 생성 및 등록

android 8.0 이상부터 필수이기 때문에 실상은 반 필수다. 어플 실행 시 android 버전을 구분한 뒤 채널을 생성해야 한다. 

 

private fun createNotificationChannel() {
    if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
        val name = getString(R.string.channel_name) // 채널 이름
        val descriptionText = getString(R.string.channel_description) // 필수는 아님
        val importance = NotificationManager.IMPORTANCE_DEFAULT // 중요도 설정
        val channel = NotificationChannel(CHANNEL_ID, name, importance).apply {
            description = descriptionText
        }
        
        // 채널을 시스템에 등록, 이후 변경 불가
        val notificationManager =
            getSystemService(Context.NOTIFICATION_SERVICE) as NotificationManager
        notificationManager.createNotificationChannel(channel)
    }
}

 

채널을 만들 때 create 메소드를 사용해서 채널의 정보들을 적어주면 된다.

 

NotificationChannel( 채널 ID, 채널 이름, 중요도)

 

이때 채널 객체를 생성 할 때 채널 ID가 BR의 이름이 되므로 이 id는 따로 보관하여(상수로 설정) 사용하는 것이 좋다. 중요도 수준은 DEFAULT외에도 여러 가지가 있는데 그 수준은 다음처럼 다양하다.

긴급
알림음이 울리며 헤드업 알림으로 표시됩니다.
IMPORTANCE_HIGH PRIORITY_HIGH 또는 PRIORITY_MAX
높음
알림음이 울립니다.
IMPORTANCE_DEFAULT PRIORITY_DEFAULT
중간
알림음이 없습니다.
IMPORTANCE_LOW PRIORITY_LOW
낮음
알림음이 없고 상태 표시줄에 표시되지 않습니다.
IMPORTANCE_MIN PRIORITY_MIN

 

채널은 만들면 바꿀 수 없지만 이용자는 어플 설정을 통해 바꿀 수 있다고 한다~

 

Notification 주요 메소드

알람 객체를 만들 때 빌더를 통해 여러가지 값을 set한다고 했다. 다음은 설정할 수 있는 속성들을 그림과 같이 살펴보기로 하자.

 

  1. setSmallIcon() : 알림을 나타내는 작은 아이콘 (필수)
  2. 어플 이름(시스템에서 제공)
  3. 타임 스탬프(시스템에서 제공) (필수) 또는 setWhen().setShowWhen(false)
  4. setLargeIcon()
  5. setContentTitle() : 알림의 제목 설정
  6. setContentText() : 알림의 세부 설명
  • setPriority() : 알림의 우선순위를 정하는데 최신 버전에서는 생략
  • setStyle() : 다양한 유형의 알림 생성 기능
  • setSubtext(), setTicked(), setWhen(), setLights()...
  • setContentIntent(intent : PendingIntent) : 알림을 선택했을 때 다른 인텐트를 실행시키는 메소드, class가 아니라 pendingIntent를 매개변수로 받는데 pendingIntent는 intent를 포장하는 용도!

 

   val intent = Intent(this, AlertDetails::class.java).apply {
        flags = Intent.FLAG_ACTIVITY_NEW_TASK or Intent.FLAG_ACTIVITY_CLEAR_TASK
    }
    val pendingIntent: PendingIntent = PendingIntent.getActivity(this, 0, intent, 0)

    val builder = NotificationCompat.Builder(this, CHANNEL_ID)
            .setSmallIcon(R.drawable.notification_icon)
            .setContentTitle("My notification")
            .setContentText("Hello World!")
            
            // 8.0 이하일 때
            .setPriority(NotificationCompat.PRIORITY_DEFAULT)
            .setContentIntent(pendingIntent)
            .setAutoCancel(true)

 

 

똑같은 엑티비티를 여러번 띄울 수 있어서 만들어진 액티비티는 스택에 쌓이는데, 만약 똑같은 액티비티가 다시 한 번 생성되면 어떻게 할지 결정하기 위해 flag를 사용한다. 해당 코드에서는 이전 액티비티말고 새로운 액티비티로 대체한다.

 

pendingIntent.getActivity(context, pendingIntent_id, PengingIntent, FLAG_IMMUTATBLE)

 

intent를 포장하고 있는 pendingIntent. 이때 엑티비티클래스를 포장하고 있어서 getActivity라는 함수를 써야한다. 다시 말해 담고 있는 내용물에 따라 사용하는 함수 이름이 달라진다. 마지막 매개변수는 IMMUTATBLE로 intent 내용을 못 바꾸게 한다는 것이다.

 

하지만 왜 intent를 그대로 사용하지 않고 pendingIntent를 사용해야 할까?

-> intent는 내 어플에서 컴포넌트끼리 통신하기 위해 사용한다. 하지만 알림은 앱 외부에서 사용하기 때문에 인텐트를 포장하는 단계가 필요하다. 

 

val notiManager = NotificationManagerCompat.from(this)
notiManager.notify(100, builder.build())

 

notiManager은 getSystemService 말고도 from으로 바로 얻어올 수 있다.

 

builder로 설정을 세팅한 뒤 .build()를 통해 Notification 객체를 생성한다. 이후 Manager에게 넘기는데 위의 코드에서는 바로 build()된 객체를 넘긴다. 이때 100은 Notification의 id이다. 

 

👻참고 SmallIcon() 설정하는 법

res > drawable 오른쪽 마우스 클릭 > New > Image asset

New > Image Asset

 

 

Clip art에 이미 만들어진 아이콘들도 있다! 나는 페스티벌 아이콘 선택함

 

 

그럼 다음처럼 ic_action_name 폴더에 아이콘 사이즈별로 들어가게 됨!

 

⁉️ 주의해야 할 점

 

알람은 mainfest에 퍼미션 등록을 해야 하지만 분명 실시간으로 사용자에게 확인을 받아야 한다고 했다. 관련 코드도 작성해야 하지만 지금 포스팅에서는 다루지 않으므로 간편(?)하게 무시하는 코드를 다음과 같이 추가시켜 준다.

@SuppressLint("MissingPermission")

 

하지만 무시만 해서 알림을 받을 수 있는 건 아니고, 코드로 설정을 안 했으니 어플 설정에 들어가서 어플 알림을 받을 수 있도록 체크해주어야 한다.

setting > notification > app settings

 

 

이용자로부터 권한 확인을 받았는지 여부를 확인하는 법
notificationManager.areNotificationEncabled()

 

 

 

확인

어플을 확인하면 다음처럼 알림이 뜨는 걸 확인할 수 있다. 그리고 알림을 누르면 alert 엑티비티가 뜬다.

 

 

작업 버튼 추가

앞선 방법과 똑같다. builder에 액션을 추가해서 탭할 때와 다른 동작을 지정한다. 예컨대 예, 아니오 같은 버튼 부터 문자 메시지 답장까지 지정해줄 수 있다. 앞서 탭했을 때 액티비티로 넘어가는 게 아니라 답장 기능을 수행해야 한다 -> 기능이 엄연히 다름. 그래도 pendingIntent에 intent를 전달해주어야 한다. 편의를 위해 intent는 아까 만들어두었던 액티비티 intent를 사용했다.

 

.addAction(R.drawable.ic_action_name, "쉬기", pendingIntent)

 

 


 

Broadcast?

가령 메시지 어플을 만들었는데 어플이 실행되지 않았는데도 수신된 메시지를 확인하고 싶다면 어떻게 해야 할까? 안드로이드 앱은 이렇듯 어플을 실행 여부를 떠나 시스템 혹은 다른 어플을 대상으로 방송 메시지를 수신/송신할 수 있다. 이 개념은 유튜브 구독 채널의 게시-구독 구조와도 유사하다. 관심 사항에 해당하는 이벤트가 발생할 때 방송을 수신 받으며, 이런 이벤트는 시스템 부팅, 충전 시작, 네트워크 상태 변경 혹은 자체 이벤트도 가능하다. 

 

방송 수신

이런 방송을 수신하고 싶다면 BroadcastReceiver를 사용해야 하는데, 특정 방송에 대한 수신 여부를 등록한다. 방송이 생성이 되었을 경우 해당 수신자 BR를 찾아 방송 내용을 전달한다. 일대일 통신이 아니기 때문에 구독(수신)하는 BR을 찾아 수신한다. 

 

방송 송신

안드로이드 시스템은 다양한 종류의 방송을 송신한다. 방송 내용은 Intent 안에 포함되며 Intent는 방송 종류(id)인 Action과 방송 데이터(내용)인 Extra로 이루어진다. 

 

방송 수신(Broadcast Receiver)

BR 설정방법 

1. AndroidManifest 기록 -> 어플 설치 이후부터 (지속적) 방송 수신

<receiver android:name=".AlertBroadcastReceiver" android:exported="true">
    <intent-filter>
        <action android:name="ACTION_SNOOZE" />
    </intent-filter>
</receiver>

<action>에 수신할 방송의 종류를 적는다. 이때 위처럼 이름만 적어서는 안 되고, FullPackageName + 방송 이름 형식으로 적어야 한다. 방송은 불특정 다수가 수신하는 정보라 다른 방송과 이름이 겹쳐서는 안 된다. 따라서 구별할 수 있는 식별자인 풀 패키지 네임을 앞에 붙여주는 것이 좋을 것이다.

 

2. 코드에서 실행 -> 어느 시점에서만 수신

 

 

BR 구현 방법

BR을 상속받는 클래스를 만들면 된다. onReceive(수신했을 때) 함수에서는 어플의 context와 방송 내용인 intent가 들어간다. 이후 수신된 intent의 getExtra로 데이터를 받아온다. 단 onReceive()는 어플이 실행되지 않는 경우에 동작되기에 짧은 시간에만 수행해야 한다.

 

 

방송 송신

방송을 직접 생성하여 송신한다. 

 

지정한 시간과 간격으로 특정 이벤트를 발생시키는 서비스. 특정 인텐트를 실행하는 서비스다. 

알람을 통해 엑티비티, br(노티를 띄운다거나) 또는 서비스 실행(일정시간 간격)

보통 br을 실행시킨 후 br에서 엑티비티 또는 서비스 실행

 

알람

시스템이 관리한다. 내가 만든 기능이 시스템에서 구동하기 때문에 이것도 pending으로 감싸야 한다. 절전 상태에서도 알람 실행할 수 있다. 장치를 재부팅하면 모든 알람이 취소되기 때문에 부팅 알람을 받을 때 알람을 다시 등록하는 작업이 필요하다. 

 

하지만 알람 서비스를 사용 시 정확한 시간을 파악하려고 하면 시스템 리소스를 많이 잡아먹게 되어 리소스 관리가 필수적이다.

 

 

알람 서비스 사용 절차

1. AlarmManager 객체 준비

getSystemService(Context.ALARM_SERVICE)를 통해 객체를 얻어올 수 있다.

 

2. PendingIntent 준비

알람 수행 시 동작할 정보를 intent를 사용해 지정한다. 위에서 PendingIntent에 설정한 것처럼 getActivity/getBroadcast/getService를 사용한다.

 

3. AlarmManager에 알람 설정 

두 가지 시간 방식을 통해 시간 설정.

 

 

알림을 등록하는 코드와 마찬가지로 알람을 취소할 때도 intnent를 넣어 취소한다.

 

알람의 시간 방식.

1. Elapsed

부팅한 시간부터 카운트된 시간. 상대적이다. 간격마다 알람을 울리고 싶으면 이 시간방식을 사용.

 Realtime : 현실 세계와 똑같이 맞춤

 

 

중요하지 않은 알람은 부정확한 알람을 사용해도 된다. 

 

알람 설정 메소드

set()

set(type(시계종류) : Int, triggerAtMills(어떤 시간 대) : 

일회성 알람. 

 

 

 

 

'모바일 > Android' 카테고리의 다른 글

[공식 문서] Lifecycle of composables  (0) 2024.08.04
Kotlin Coroutine  (0) 2024.07.02
Suspicious indentation: This is indented but is not continuing the previous expression  (0) 2024.06.15
SQLite  (0) 2024.06.14
제트팩 라이브러리  (0) 2024.06.14

댓글