#1 개요
앱에게 있어 알림(Notifications)는 빼놓을 수 없는 구성 요소다. 알림을 기초적으로 구현해본다.
#2 SDK, 권한, View 설정
#2-1 최소 SDK 설정
Minimum SDK를 API 기준 26 이상으로 설정한다. Notifications 대부분 기능의 최소 요구 API가 26이기 때문이다.
#2-2 AndroidManifest.xml에서 알림 권한 설정
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools">
<uses-permission android:name="android.permission.POST_NOTIFICATIONS" />
<application
...
</application>
</manifest>
알림 발생에 대한 권한을 허용해준다.
#2-3 View 구현
<?xml version="1.0" encoding="utf-8"?>
<androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
tools:context=".MainActivity">
<TextView
android:id="@+id/textView"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="MainActivity"
android:textSize="40dp"
app:layout_constraintBottom_toTopOf="@id/button"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent" />
<Button
android:id="@+id/button"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="Trigger Notification"
android:textSize="30dp"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="@+id/textView" />
</androidx.constraintlayout.widget.ConstraintLayout>
TextView 하나와 Button 하나가 있는 간단한 View를 구성한다.
#3 MainActivity.kt 구현
#3-1 NotificationManager 할당
// package com.example.notificationsbasics
...
class MainActivity : AppCompatActivity() {
private var notificationManager: NotificationManager? = null
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)
val button = findViewById<Button>(R.id.button)
notificationManager = getSystemService(Context.NOTIFICATION_SERVICE) as NotificationManager
}
}
그 다음은 알림을 발생시키는 클래스인 NotificationManager의 인스턴스를 할당한다. NotificationManager는 System Service의 일종으로, Context.getSystemService(name: String)로 인스턴스를 가져올 수 있다. 프로퍼티 notificationManager를 val이 아닌 var로 선언하는 이유는, 액티비티의 생명주기 동안 onCreate()가 여러 번 실행될 여지가 있고, 따라서 그 때마다 getSystemService(name: String)가 재할당될 가능성 또한 생기기 때문이다.
#3-2 NotificationChannel
...
class MainActivity : AppCompatActivity() {
private var notificationManager: NotificationManager? = null
private val channelId = "com.example.notificationsbasics.channel1"
override fun onCreate(savedInstanceState: Bundle?) {
...
notificationManager = getSystemService(Context.NOTIFICATION_SERVICE) as NotificationManager
createNotificationChannel(channelId, "My Channel", "It's my channel")
}
private fun createNotificationChannel(id: String, name: String, channelDescription: String) {
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) { // 숫자 0이 아니라 Oreo의 첫 글자인 알파벳 대문자 O다.
val importance = NotificationManager.IMPORTANCE_HIGH
val channel = NotificationChannel(id, name, importance).apply {
description = channelDescription
}
notificationManager?.createNotificationChannel(channel)
}
}
}
본격적으로 Notifications 코드를 짤 때 우선적으로 해야하는 작업은 바로 NotificationChannel을 할당하는 것이다. NotificationChannel 객체의 존재 이유는 알림의 카테고리화다. 메신저 앱을 예로 들면, 메시지 도착 알림, 그룹 채팅방 초대 알림, 친구 추가 알림 등은 서로 구분되는 목적과 중요도를 지닌다. NotificationChannel이 바로 그러한 카테고리화를 수행한다.
프로그래머 입장에서 카테고리별로 나뉘는 알림마다 알림 스타일, 중요도 등을 일괄적으로 설정해줄 수 있다는 이점이 있다. 사용자 입장에서는 정제된 사용자 경험을 선사하고, 특정 유형의 카테고리에 대한 알림을 명시적으로 수신 거부하기 편리하게 만들어준다.
'사용자 정의 알림 채널'이라는 개념 자체가 API 26부터 생겨났기에 API 26(안드로이드 Oreo)부터 NotificationChannel이 지원된다. API가 26 미만인 안드로이드 OS에서는 if문에 의해 createNotificationChannel()가 수행되지 않으며, 기본값으로 설정된 채널에 알림이 수신된다.
#3-3 NotificationManager.notify() 구현
...
class MainActivity : AppCompatActivity() {
...
override fun onCreate(savedInstanceState: Bundle?) {
...
notificationManager = getSystemService(Context.NOTIFICATION_SERVICE) as NotificationManager
createNotificationChannel(channelId, "My Channel", "It's my channel")
button.setOnClickListener {
displayNotification()
}
}
private fun displayNotification() {
val notificationId = 777 // 알림 채널 Id와는 또 다른 알림 하나하나의 Id. 동일한 Id의 알림이 상태바에 있을 때, 같은 Id인 알림을 발생시키면, 원래 있던 알림이 최신 알림으로 갈아끼워진다.
val notification = NotificationCompat.Builder(this@MainActivity, channelId).apply {
setContentTitle("My Notification Title") // 상태바 내리면 화면에 보일 알림 제목
setContentText("It's my notification") // 상태바 내리면 화면에 보일 알림 본문
setSmallIcon(android.R.drawable.ic_dialog_info) // 알림 아이콘 설정
setAutoCancel(true) // 상태바에서 알림 클릭 시, 알림이 사라지게 만드는 옵션 (하지만, PendingIntent가 없으면 옵션 적용 안됨)
setPriority(NotificationCompat.PRIORITY_HIGH) // API 26 미만을 위한 알림 중요도 옵션. API 26 이상부터는 NotificationChannel에 할당한 알림 중요도가 우선시된다.
}.build()
notificationManager?.notify(notificationId, notification)
}
private fun createNotificationChannel(id: String, name: String, channelDescription: String) {
...
}
}
버튼을 누르면 알림이 상태바에 게시되도록 만든다. 여기서는 알림 Id를 행운의 수인 777로 정해주었으나, 실제 앱을 개발할 때는 체계적인 규칙을 정해두고 신중하게 설정해주어야 할 것이다. NotificationManager.notify()의 두 번째 인자인 Notification을 AndroidX의 라이브러리에 속하는 NotificationCompat의 빌더 클래스 NotificationCompat.Builder를 통해 생성했다. 메소드 체이닝으로 NotificationCompat.Builder 객체를 연쇄적으로 만들고, 해당 객체를 build()하여 Notification 객체 notification을 만들고 있는 모습을 볼 수 있다.
메소드 체이닝 구문 중에 있는 autoCancel을 true로 설정해도, PendingIntent가 없다면 상태바에서 알림을 클릭해도 알림이 사라지지 않는다. 다음 게시글에서 NotificationCompat.Builder.setContentIntent(intent: PendingIntent?)를 통해 PendingIntent를 할당해보겠다.
#4 작동 확인
만약 버튼을 눌러도 알림이 뜨지 않는다면, 설치된 앱의 알림 권한이 차단되지는 않았는지 확인해보자. 기본값 설정이 차단인 경우가 있다.
#5 완성된 앱
#6 이어지는 글
본 게시글을 기반으로 알림 클릭 시의 동작을 구현해본다.
'깨알 개념 > Android' 카테고리의 다른 글
[Android] Notifications - Action 버튼 클릭 시 알림 Cancel (0) | 2024.06.14 |
---|---|
[Android] Notifications - PendingIntent (0) | 2024.06.12 |
[Android] Retrofit - Post (0) | 2024.06.11 |
[Android] Retrofit - Logging, Timeout 관리 (Interceptor) (0) | 2024.06.11 |
[Android] Retrofit - MVVM 구조 (0) | 2024.06.05 |