예제를 살펴보기 전에, 먼저 안드로이드에서 서비스(Service)가 무엇인지 이해할 필요가 있습니다. 서비스는 UI와 상호작용하지 않은 채 백그라운드에서 작업을 수행하는 컴포넌트로, 액티비티가 종료된 후에도 계속해서 동작합니다.
START_STICKY란 무엇인가?
START_STICKY는 onStartCommand() 메서드의 반환 타입 중 하나입니다. 서비스가 START_STICKY로 시작되면 액티비티가 포그라운드에 있지 않아도 백그라운드에서 계속 동작하며, 시스템이 메모리 부족 등의 이유로 서비스를 강제 종료하더라도 사용자의 개입 없이 자동으로 서비스를 다시 시작합니다.
이 글에서는 실제 예제를 통해 서비스에 START_STICKY를 구현하는 방법을 단계별로 알아보겠습니다.
1단계: 새 프로젝트 생성
Android Studio에서 File → New Project를 선택하고, 필요한 모든 정보를 입력하여 새 프로젝트를 생성합니다.
2단계: 레이아웃 파일 작성
res/layout/activity_main.xml에 아래 코드를 추가합니다.
<?xml version = "1.0" encoding = "utf-8"?>
<android.support.constraint.ConstraintLayout xmlns:android = "https://schemas.android.com/apk/res/android"
xmlns:app = "https://schemas.android.com/apk/res-auto"
xmlns:tools = "https://schemas.android.com/tools"
android:layout_width = "match_parent"
android:layout_height = "match_parent"
tools:context = ".MainActivity">
<TextView
android:id = "@+id/text"
android:layout_width = "wrap_content"
android:layout_height = "wrap_content"
android:text = "Start Service"
android:textSize = "25sp"
app:layout_constraintBottom_toBottomOf = "parent"
app:layout_constraintLeft_toLeftOf = "parent"
app:layout_constraintRight_toRightOf = "parent"
app:layout_constraintTop_toTopOf = "parent" />
</android.support.constraint.ConstraintLayout>
위 코드에는 TextView 하나가 배치되어 있습니다. 사용자가 이 TextView를 클릭하면 서비스가 시작되거나 중지됩니다.
3단계: MainActivity 작성
src/MainActivity.java에 아래 코드를 추가합니다.
package com.example.andy.myapplication;
import android.app.ActivityManager;
import android.content.Context;
import android.content.Intent;
import android.support.v7.app.AppCompatActivity;
import android.os.Bundle;
import android.util.Log;
import android.view.View;
import android.widget.TextView;
public class MainActivity extends AppCompatActivity {
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
final TextView text = findViewById(R.id.text);
text.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
if (isMyServiceRunning(service.class)) {
text.setText("Stoped");
stopService(new Intent(MainActivity.this, service.class));
} else {
text.setText("Started");
startService(new Intent(MainActivity.this, service.class));
}
}
});
}
private boolean isMyServiceRunning(Class<?> serviceClass) {
ActivityManager manager = (ActivityManager) getSystemService(Context.ACTIVITY_SERVICE);
for (ActivityManager.RunningServiceInfo service : manager.getRunningServices(Integer.MAX_VALUE)) {
if (serviceClass.getName().equals(service.service.getClassName())) {
return true;
}
}
return false;
}
}
위 코드는 인텐트(Intent)에 컨텍스트와 서비스 클래스를 전달하여 서비스를 시작하고 중지하는 역할을 합니다. isMyServiceRunning() 메서드를 통해 현재 서비스 실행 여부를 확인한 뒤, 실행 중이라면 중지하고, 그렇지 않다면 시작하도록 처리했습니다.
서비스 클래스 생성
이제 패키지 폴더 내에 service.class라는 이름의 서비스 클래스를 생성하고 아래 코드를 추가합니다.
package com.example.andy.myapplication;
import android.annotation.TargetApi;
import android.app.Notification;
import android.app.NotificationChannel;
import android.app.NotificationManager;
import android.app.PendingIntent;
import android.app.Service;
import android.content.Context;
import android.content.Intent;
import android.graphics.Color;
import android.media.MediaPlayer;
import android.os.Build;
import android.os.IBinder;
import android.support.annotation.RequiresApi;
import android.support.v4.app.NotificationCompat;
import android.util.Log;
import android.widget.Toast;
public class service extends Service {
@Override
public IBinder onBind(Intent intent) {
return null;
}
@Override
public void onCreate() {
super.onCreate();
}
@TargetApi(Build.VERSION_CODES.O)
@RequiresApi(api = Build.VERSION_CODES.JELLY_BEAN)
@Override
public int onStartCommand(Intent intent, int flags, int startId) {
Toast.makeText(this, "Notification Service started by user.", Toast.LENGTH_LONG).show();
Log.d("Tutorialspoint.com","Services is working background");
return START_STICKY;
}
@RequiresApi(api = Build.VERSION_CODES.N)
@Override
public void onDestroy() {
super.onDestroy();
Toast.makeText(this, "Notification Service destroyed by user.", Toast.LENGTH_LONG).show();
}
}
핵심은 onStartCommand() 메서드에서 START_STICKY를 반환한다는 점입니다. 이렇게 하면 시스템에 의해 서비스가 종료되어도 자동으로 재시작됩니다. 아래 이미지처럼 서비스 정보를 확인할 수 있습니다.

4단계: 매니페스트 등록
manifest.xml에 아래 코드를 추가하여 서비스를 선언합니다.
<?xml version = "1.0" encoding = "utf-8"?>
<manifest xmlns:android = "https://schemas.android.com/apk/res/android"
package = "com.example.andy.myapplication">
<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>
<service android:name = ".service"/>
</application>
</manifest>
서비스를 사용하려면 반드시 매니페스트 파일에 <service> 태그로 선언해야 한다는 점을 잊지 마세요.
애플리케이션 실행 및 결과 확인
이제 애플리케이션을 실행해 보겠습니다. 실제 안드로이드 기기가 컴퓨터에 연결되어 있다고 가정합니다. Android Studio에서 프로젝트의 액티비티 파일 중 하나를 연 뒤 툴바의 Run 아이콘을 클릭하고, 테스트할 모바일 기기를 선택하세요. 그러면 기기에 아래와 같은 초기 화면이 표시됩니다.

초기 화면에서 TextView를 클릭하면 아래와 같이 알림(Notification) 서비스가 시작됩니다.

서비스가 시작된 상태에서 다시 TextView를 클릭하면 아래와 같이 알림 서비스가 중지됩니다.

마무리
이상으로 안드로이드 서비스에서 START_STICKY를 구현하는 방법을 알아보았습니다. START_STICKY를 활용하면 시스템에 의해 서비스가 강제 종료되더라도 자동으로 재시작되므로, 음악 재생, 위치 추적, 데이터 동기화처럼 지속적인 백그라운드 작업이 필요한 앱을 개발할 때 유용하게 사용할 수 있습니다.