이 튜토리얼에서는 Kotlin을 사용하여 Android 앱의 서비스(Service)에서 알림(Notification)을 전송하는 방법을 단계별로 알아봅니다. 백그라운드에서 실행되는 포그라운드 서비스(Foreground Service)를 만들고, 사용자가 입력한 텍스트를 알림으로 표시하는 예제 앱을 함께 구현해 보겠습니다.
구현 개요
이 예제에서는 다음과 같은 흐름으로 동작하는 앱을 만듭니다.
- 사용자가 EditText에 텍스트를 입력합니다.
- '서비스 시작' 버튼을 누르면 포그라운드 서비스가 실행되고, 입력한 내용이 담긴 알림이 상태 바에 표시됩니다.
- '서비스 중지' 버튼을 누르면 서비스와 알림이 함께 종료됩니다.
참고: Android 8.0(API 26) 이상에서는 알림을 표시하려면 반드시 알림 채널(Notification Channel)을 생성해야 하며, 포그라운드 서비스를 사용하려면 매니페스트에 권한 선언이 필요합니다.
1단계 – 새 프로젝트 생성
Android Studio에서 File → New Project로 이동한 뒤, 빈 Activity(Empty Activity) 템플릿으로 새 프로젝트를 생성하고 필요한 정보를 모두 입력합니다. 언어는 Kotlin으로 선택하세요.
2단계 – 레이아웃 작성 (activity_main.xml)
res/layout/activity_main.xml 파일에 아래 코드를 추가합니다. 텍스트 입력창과 두 개의 버튼(서비스 시작/중지)으로 구성된 세로 방향 LinearLayout입니다.
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="https://schemas.android.com/apk/res/android"
xmlns:tools="https://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical"
tools:context=".MainActivity">
<EditText
android:id="@+id/editText"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:hint="Input" />
<Button
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:onClick="startService"
android:text="Start Service" />
<Button
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:onClick="stopService"
android:text="Stop Service" />
</LinearLayout>3단계 – MainActivity.kt 작성
src/MainActivity.kt에 아래 코드를 추가합니다. 'Start Service' 버튼 클릭 시 입력값을 Intent에 담아 포그라운드 서비스를 시작하고, 'Stop Service' 버튼 클릭 시 서비스를 종료합니다.
import android.content.Intent
import android.os.Bundle
import android.view.View
import android.widget.EditText
import androidx.appcompat.app.AppCompatActivity
import androidx.core.content.ContextCompat
class MainActivity : AppCompatActivity() {
lateinit var editText: EditText
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)
title = "KotlinApp"
editText = findViewById(R.id.editText)
}
fun startService(view: View) {
val input: String = editText.text.toString()
val serviceIntent = Intent(this, ExampleService::class.java)
serviceIntent.putExtra("inputExtra", input)
ContextCompat.startForegroundService(this, serviceIntent)
}
fun stopService(view: View) {
val serviceIntent = Intent(this, ExampleService::class.java)
stopService(serviceIntent)
}
}핵심 포인트: Android 8.0(API 26)부터는 일반 startService() 대신 ContextCompat.startForegroundService()를 사용해야 합니다. 이 메서드로 시작된 서비스는 반드시 짧은 시간 안에 startForeground()를 호출해야 시스템에서 ANR 오류가 발생하지 않습니다.
4단계 – 서비스 클래스 작성 (ExampleService.kt)
새 Kotlin 클래스 ExampleService.kt를 생성하고 아래 코드를 추가합니다. 이 클래스가 핵심으로, 알림 채널 생성과 알림 표시를 담당합니다.
import android.app.*
import android.content.Intent
import android.os.Build
import android.os.IBinder
import androidx.annotation.RequiresApi
import androidx.core.app.NotificationCompat
class ExampleService : Service() {
private val channelId = "Notification from Service"
@RequiresApi(Build.VERSION_CODES.O)
override fun onCreate() {
super.onCreate()
if (Build.VERSION.SDK_INT >= 26) {
val channel = if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
NotificationChannel(
channelId,
"Channel human readable title",
NotificationManager.IMPORTANCE_DEFAULT
)
} else {
TODO("VERSION.SDK_INT < O")
}
(getSystemService(NOTIFICATION_SERVICE) as NotificationManager).createNotificationChannel(
channel
)
}
}
override fun onStartCommand(intent: Intent, flags: Int, startId: Int): Int {
val input = intent.getStringExtra("inputExtra")
val notificationIntent = Intent(this, MainActivity::class.java)
val pendingIntent = PendingIntent.getActivity(
this,
0, notificationIntent, 0
)
val notification: Notification = NotificationCompat.Builder(this, channelId)
.setContentTitle("Example Service")
.setContentText(input)
.setSmallIcon(R.drawable.notification)
.setContentIntent(pendingIntent)
.build()
startForeground(1, notification)
return START_NOT_STICKY
}
override fun onBind(p0: Intent?): IBinder? {
return null
}
}코드 설명:
onCreate(): API 26 이상에서만 알림 채널을 생성합니다. 채널 ID, 사용자에게 표시될 이름, 중요도(IMPORTANCE_DEFAULT)를 지정합니다.onStartCommand(): MainActivity에서 전달받은 입력값을 알림 본문(setContentText)으로 설정하고, 알림을 탭하면 앱으로 돌아올 수 있도록 PendingIntent를 연결합니다.startForeground(1, notification): 서비스를 포그라운드 상태로 승격시켜 알림을 지속적으로 표시합니다.START_NOT_STICKY: 시스템에 의해 종료된 후 자동으로 재시작하지 않도록 설정합니다.
5단계 – AndroidManifest.xml 설정
androidManifest.xml에 포그라운드 서비스 권한을 추가합니다. 이 권한이 없으면 서비스가 정상적으로 실행되지 않습니다.
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="https://schemas.android.com/apk/res/android" package="com.example.q11">
<uses-permission android:name="android.permission.FOREGROUND_SERVICE"/>
<application
android:allowBackup="true"
android:icon="@mipmap/ic_launcher"
android:label="@string/app_name"
android:roundIcon="@mipmap/ic_launcher_round"
android:supportsRtl="true"
android:theme="@style/AppTheme">
<activity android:name=".MainActivity">
<intent-filter>
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.LAUNCHER" />
</intent-filter>
</activity>
</application>
</manifest>참고: Android 13(API 33) 이상을 대상으로 하는 경우, 런타임 알림 권한인 android.permission.POST_NOTIFICATIONS도 함께 선언하고 사용자에게 요청해야 알림이 표시됩니다.앱 실행 및 결과 확인
이제 애플리케이션을 실행해 보겠습니다. 실제 Android 기기를 컴퓨터에 연결했다고 가정합니다. Android Studio에서 프로젝트의 액티비티 파일 중 하나를 연 뒤, 툴바에서 Run 아이콘
을 클릭합니다. 목록에서 연결된 모바일 기기를 선택하면, 기본 화면이 기기에 표시됩니다.
EditText에 원하는 텍스트를 입력하고 Start Service 버튼을 누르면 아래와 같이 서비스에서 발송한 알림이 상태 바에 나타납니다.


마무리
지금까지 Kotlin으로 Android 서비스에서 알림을 보내는 전체 과정을 살펴보았습니다. 핵심은 다음 세 가지입니다.
- 포그라운드 서비스 권한을 매니페스트에 선언할 것
- API 26 이상에서는 알림 채널을 먼저 생성할 것
startForegroundService()로 서비스를 시작한 후startForeground()를 호출할 것
이 패턴은 음악 재생, 위치 추적, 파일 다운로드 등 장시간 백그라운드 작업이 필요한 거의 모든 앱에서 활용되므로, 잘 익혀두면 다양한 프로젝트에 응용할 수 있습니다.