안드로이드의 알림(Notification)은 앱이 사용자에게 정보를 전달하고, 상호작용을 유도하는 중요한 부분입니다.
그럼 이번 시간에는 Notification에 대해서 알아보도록 합니다.
Notification?
- 상태바에 앱의 상태를 출력하여 유저에게 알림을 보내는 기능입니다. 이를 통해 사용자는 앱 외부에서도 알림을 받을 수 있습니다.
- 시스템에 의해 관리되기 때문에, 액티비티에서 직접적인 제어가 불가능합니다.
Notification 프로세스 흐름 (flowchart)
Notification의 흐름을 나타낸 Flowchart입니다.
이걸 바탕으로 간단하게 설명하겠습니다.
- NotificationChannel을 생성합니다.
- 안드로이드 8.0(API 26)부터는 알림을 전송하기 전에 ‘NotificationChannel’을 먼저 생성해야합니다.
- 이 채널은 알림의 중요도(Priority), 사운드, 진동 패턴 등의 세부적인 설정을 관리합니다.
- NotificationCompat.Builder로 알림을 생성합니다.
- NotificationCompat.Builder를 사용하여 알림을 구성하는데, 이때 알림을 만들기 위해 필요한 정보(제목, 내용, 아이콘)와 앞서 생성안 NotificationChannel의 ID를 지정해야합니다.
- Notification 객체 생성하기
- NotificationCompat.Builder의
build()
메서드를 호출하여 Notification 객체를 생성합니다.
- NotificationCompat.Builder의
- NotificationManager로 알림 표시
- NotificationManager의
notify()
메서드를 호출하여 사용자에게 알림을 표시합니다.
- 각 알림에는 앞선 단계에서 지정 하듯 고유한 ID가 필요한데, 동일한 ID로 알림을 다시 표시하면 기존 알림이 새로운 내용으로 업데이트 됩니다.
- NotificationManager의
본격적으로 예제를 보기 전에, NotificationChannel과 NotificationCompat.Builder, NotificationManager, Notification 객체를 각각 자세히 짚어보고 갈께요.
조금 헷갈리지만, Notification이 대부분의 앱에 구현되는 만큼, 한번 짚고넘어가면 많은 도움이 될거라고 생각합니다.
☑️ NotificationChannel
NotificationChannel(id: String!, name: CharSequence!, importance: Int)
- 앱의 환경설정에서 Notification의 ON-OFF를 설정할 수 있는데, 이때 사용되는 개념이 바로 '채널'이죠.
- 안드로이드 8.0 (API 레벨 26) 이상에서 도입되었습니다.
- 알림을 전송하기 전에NotificationChannel을 생성해야 합니다.
- NotificationChannel은 사용자가 알림의 동작을 조정할 수 있게 해주는 메커니즘입니다.
- NotificationManager를 사용해 시스템에 등록됩니다.
- 중요도: 각 채널은 특정 중요도를 가집니다. 중요도에 따라 알림의 표시 방식이 달라집니다.
- NotificationManager.IMPORTANCE_HIGH: 긴급 상황 & 알림음이 울리며 헤드업으로 표시
- NotificationManager.IMPORTANCE_DEFAULT: 높은 중요도 & 알림음이 울림.
- NotificationManager.IMPORTANCE_LOW: 중간 중요도 & 알림음이 울리지 않음.
- NotificationManager.IMPORTANCE_MIN: 낮은 중요도 & 알림음이 없고 상태표시줄에도 표시되지 않음.
- 사운드: 사용자가 채널별로 알림 소리를 설정할 수 있습니다.
- 진동: 채널별로 진동 패턴을 설정할 수 있습니다.
☑️ NotificationCompat.Builder
Builder(context: Context!) - API Level 26 (Android 8) 이전 버전
Builder(context: Context!, channelled: String!) - API Level 26 (Android 8) 부터 사용되는 형태.
- NotificationCompat.Builder는 안드로이드 알림을 구축하기 위한 클래스이며, 이를 사용해서 다양한 버전의 안드로이드에서 호환되는 알림을 쉽게 만들 수 있습니다.
- 알림 내용 설정: 제목, 텍스트, 아이콘 등 알림의 기본 내용을 설정합니다.
- 알림 액션 설정: 사용자가 알림을 탭했을 때 실행될 인텐트(Intent)를 설정합니다.
- 알림 스타일 설정: 큰 이미지, 긴 텍스트 등 특별한 형식의 알림을 만들 수 있습니다.
- 호환성: NotificationCompat.Builder를 사용하면, 다양한 버전의 안드로이드에서 일관된 방식으로 알림을 표시할 수 있습니다.
- NotificationChannel과 함께 사용하면, NotificationCompat.Builder는 해당 채널의 설정을 따르는 알림을 만듭니다.
- 이 두 컴포넌트를 함께 사용하면, 앱의 알림을 사용자 친화적이고 유연하게 관리할 수 있으며, 다양한 안드로이드 버전과의 호환성도 보장할 수 있습니다.
☑️ NotificationChannel
- 알림을 사용자에게 표시하거나 취소하는 역할을 합니다.
- NotificationManager의
notify()
메소드를 사용하여 NotificationCompat.Builder로 생성된 Notification 객체를 표시합니다.
- 또한, NotificationChannel을 시스템에 등록하거나 조회하는 데도 사용됩니다.
요약하면,
NotificationChannel
은 알림의 특성을 정의하고, NotificationCompat.Builder
는 실제 알림을 구성하며, NotificationManager
는 알림을 시스템에 표시하거나 관리하는 역할을 합니다.
이번 예제에서는 가장 기본적인 알림만 보도록 하겠습니다.
Progress, Image, Action이 포함된 notification은 다음 글에서 다루도록 하겠습니다.
히히 이미지가 되게 산만해졌군요.
그래도 뭐가 뭔지 대충은 이해가 가시리라 생각합니다.
아래 코드는 위 Notification의 코드입니다.
fun initializeNotification() {
val builder = NotificationCompat.Builder(this, "1")
.setSmallIcon(R.mipmap.ic_launcher)
.setStyle(NotificationCompat.BigTextStyle().bigText("설정을 보려면 누르세요.")
.setBigContentTitle(null)
.setSummaryText("서비스 동작중")
)
.setWhen(System.currentTimeMillis())
.setContentTitle("서비스 알림 타이틀")
.setContentText("서비스 알림 내용")
.setOngoing(true)
.setShowWhen(true)
val notificationIntent = Intent(this, MainActivity::class.java)
val pendingIntent = PendingIntent.getActivity(this, 0, notificationIntent, PendingIntent.FLAG_IMMUTABLE)
builder.setContentIntent(pendingIntent)
val manager = getSystemService(Context.NOTIFICATION_SERVICE) as NotificationManager
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
manager.createNotificationChannel(
NotificationChannel(
"1",
"포그라운드 서비스",
NotificationManager.IMPORTANCE_NONE
)
)
}
val notification = builder.build()
manager.notify(1, notification)
}
참 쉽죠?
이제 부분부분 코드를 뜯어가며 살펴보도록 하겠습니다.
🔎 NotificationCompat.Builder()
val builder = NotificationCompat.Builder(this, "1")
.setSmallIcon(R.mipmap.ic_launcher)
.setStyle(NotificationCompat
.BigTextStyle()
.bigText("설정을 보려면 누르세요.")
.setBigContentTitle("서비스 알림 내용")
.setSummaryText("서비스 동작중")
)
.setWhen(System.currentTimeMillis())
.setOngoing(true)
.setShowWhen(true)
- NotificationCompat.Builder(this,”1”):
Builder
의 생성자입니다. 첫번째 인자로 Context를, 두 번째 인자로는 알림 채널ID를 받습니다. 저는 여기서 1이라는 ID를 사용하고있습니다. 이유는 딱히 없습니다. 본인이 관리하기 편한 ID를 입맛에 맛게 설정하면 됩니다.
- setSmallIcon(R.minpmap.ic_launcher): 알림에 표시될 작은 아이콘을 설정합니다. 저는 앱의 런처 아이콘을 사용하였지만, 보통은 개발 중인 앱의 아이콘을 사용합니다.
- setStyle(…): 알림의 스타일을 설정합니다. 저는 BigTextStyle을 사용하여 확장된 텍스트 형식의 알림을 만들었습니다.(해당 스타일은 긴 텍스트를 표시할 때 유용합니다.)
- bigText(”설정 어쩌구..”): 확장된 알림의 내용을 설정합니다.
- setBigContentTitle(”서비스…”): 확장된 알림의 제목을 설정합니다.
- setSummaryText(”서비스 동작중”): 알림의 요약 텍스트를 설정하며, 주로 setWhen()으로 설정될 시간 옆에 표시됩니다.
- setWhen(System.currentTimeMillis()): 알림이 발생한 시간을 설정합니다. 저는 보통 현재 시간을 사용하는 편입니다.
- setShowWhen(Boolean): 알림이 발생한 시간을 알림에 표시할지 여부를 설정합니다. false로 설정하면 시간 안뜹니다.
- setOngoing(Boolean): 진행 중인 알림으로 설정합니다. 이렇게 ‘진행 중’으로 설정된 알림은 사용자가 쉽게 취소할 수 없습니다. 간단하게 말하면 스와이프해도 안없어집니다.
Builder의 메서드에는 이외에도 기본 제목을 설정하는 setContentTitle, 기본 내용을 설정하는 setContentText, setContentIntent, addAction, setPriority, setSound, setVibrate, setAutoCancel 등이 있습니다.
setStyle 메서드의 다른 스타일들은 부록 글에서 다루도록 하겠습니다.
🔎 알림을 탭한 경우를 위한 Intent와 PendingIntent
- Intent 설정을 보겠습니다.
val notificationIntent = Intent(this, MainActivity::class.java)
- Intent 객체를 생성하여 MainActivity를 시작하도록 설정했습니다.
- this는 현재의 Context(일반적으로 Activity나 Service)를 나타냅니다.
- PedingIntent를 보겠습니다.
val pendingIntent = PendingIntent.getActivity(this, 0, notificationIntent, PendingIntent.FLAG_IMMUTABLE)
- PendingIntent는 Intent를 포장하여 다른 어플리케이션에 전달하거나, 나중에 실행할 수 있도록 합니다. 여기서는 알림을 통해 MainActivity를 시작하기 위해 사용했습니다.
- getActivity()는 액티비티 시작을 위한 PendingIntent를 반환합니다.
- this는 Context.
- ‘0’은 PendingIntent의 요청 코드로, 이 코드를 사용하여 나중에 동일한 PendingIntent를 검색하거나 취소할 수 있습니다.
- PendingIntent.FLAG_IMMUTABLE은 PendingIntent의 내용이 변경되지 않도록 플래그를 설정합니다.이것은 보안과 관련되어 개발자들에게 권장되는 설정이기도 합니다.
- Builder에 PendingIntent를 설정
builder.setContentIntent(pendingIntent)
- NotificationCompat.Builder에 PendingIntent를 설정하여, 사용자가 알림을 탭한 경우 해당 PendingIntent가 실행되도록 합니다. 이 코드에서는 저의 MainActivity가 실행되죠.
🔎 시스템에서 NotificationManager를 가져오기
val manager = getSystemService(Context.NOTIFICATION_SERVICE) as NotificationManager
- getSystemService(Context.NOTIFICATION_SERVICE):
- getSystemService는 안드로이드 시스템 서비스를 반환하는 메서드립니다.
- Context.NOTIFICATION_SERVCE는 NotificationManager 서비스를 가져오기 위한 상수입니다. 이 서비스는 알림을 보내고 관리하는 기능을 담당합니다.
- as NotificationManager:
- Kotlin의 타입 캐스팅을 사용하여 반환된 Object를 NotificationManager타입으로 캐스팅합니다.
- getSystemService 메서드는 Object 타입을 반환하기 때문에, 사용하려는 서비스의 타입에 맞게 캐스팅이 필요합니다.
- 이 코드의 결과로 제가 선언한 manager 변수에 NotificationManager 인스턴스가 할당됩니다. 이 manager를 사용하여 알림을 표시하거나 관리할 수 있죠.
🔎 알림 채널 생성하기
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
...
}
- 해당 조건문은, 현재 이 코드를 실행 중인 안드로이드 OS의 버전이 8 이상인지를 확인하기 위해 작성하였습니다.
- 안드로이드 8 이후부터는 알림을 보내기 전에 해당 알림이 속할 NotificationChannel을 먼저 생성하야 합니다.
manager.createNotificationChannel(
NotificationChannel(
"1",
"포그라운드 서비스",
NotificationManager.IMPORTANCE_NONE
)
)
- manager.createNotificationChannel(…): NotificationManager의 메서드로, 알림 채널을 시스템에 등록하는 역할을 합니다.
- NotificationChannel(”1”, “포그라운…”, NotificationManager.IMPORTANCE_NONE):
- NotificationChannel의 생성자를 호출하여 새로운 알림 채널을 생성합니다.
- 1은 ID
- 포그라운드 뭐시기는 제가 이거 원래 다른 예제 작성한거에서 긁어온가라ㅎㅎ; 사용자게에 표시되는 채널의 이름입니다. 설정에서 알림 채널을 볼때 이 이름이 표시됩니다.
- NotificationManager.IMPORTANCE_NONE: 채널의 중요도를 나태냅니다. IMPORTANCE_NONE은 알림 소리나 화면 깨우기등이 없이 그냥 알림이 조용이 전달만 됩니다.
이 코드를 통해, 안드로이드 8버전에서 필요한 알림 채널을 생성하고, 이 채널은 ID가 1이며 “포그라운드 서비스”가 이름인 조용한 알림 채널로 설정됩니다.
🔎 알림 구축 및 표시
- 알림 구축
val notification = builder.build()
- builder는 앞서 위에서 작성된 NotificationCompat.Builder의 인스턴스입니다.
build()
메서드는 builder에서 설정한 내용을 기반으로 Notification 객체를 생성합니다.
- 생성된 Notification 객체는 notification 변수에 할당했습니다.
- 알림 시작
manager.notify(1, notification)
- 첫 번째 인자인 1은 Id인건 이제 설명이 없어도 파악 되시죵?
- 두번째 인자는 앞서 구축한 Notification 객체입니다.
- 뭐 사실,
manager.notify(1, builder.build())
이렇게 작성해도 상관 없지만, 저는 위처럼 변수에 한번 할당하는 것이 가독성 측면에서 좋아서 그냥 저렇게 하고있습니다.
자, 이제 알림이 사용자에게 즉시 표시되게 됩니다.
알림이 탭되거나 스와이프되는 등의 사용자 상호작용을 처리하려면, 앞서 제가 설명한 PendingIntent
나 NotificationCompat.Builder의 addAction()
을 통해 다음 동작을 미리 설정해 두어야합니다.
오늘도 길고 허접한 글 읽어주셔서 감사합니다.
이 글을 찾아오신 여러분께 조금이라도 도움이 되셨으면 좋겠습니다.
Uploaded by N2T
'Android > Basic' 카테고리의 다른 글
[Android: Basic] 포그라운드 서비스 ( Foreground Service ) 개념 (4) | 2023.08.31 |
---|---|
[Android: Basic] NotificationCompat.Builder의 Style (0) | 2023.08.23 |
Moshi Converter (0) | 2023.02.25 |
[Android: Jetpack] LiveData & Observer Pattern (0) | 2023.01.17 |
[Android: Jetpack] Architecture Pattern과 Android App Architecture (1) | 2023.01.12 |