버미

[Android] 컴포넌트 - 서비스(Service) 본문

안드로이드

[Android] 컴포넌트 - 서비스(Service)

Bum_2 2024. 7. 22. 08:50

Service 란?

백그라운드에서 오랜 시간동안 작업을 수행할 수 있는 어플리케이션 컴포넌트

 

  • 사용자에게 UI를 제공하지 않는다.
  • 어플리케이션 컴포넌트와 Service를 바인딩하여 상호작용할 수 있고, 프로세스간 통신(IPC)도 가능하다.
  • Service는 별개의 프로세스나 스레드가 아니다. 
    (특별히 지정하지 않은 경우 메인스레드에서 실행된다. 따라서 긴 작업을 수행하려는 경우 IntentService, JobIntentService, WorkManager을 고려할 수 있다.)
  • Service는 앱에서 오직 1개의 인스턴스만 생성가능하다

Service  종류

 

1. Foreground Service

  • 사용자에게 보이는 작업을 수행한다.
  • 알림창을 통해 Service가 실행중인 것을 나타낸다. 
    (이 알림은 서비스가 멈추거나 Foreground에서 서비스가 제거되기 전까지 해제할 수 없다.)
  • 사용자가 앱과 상호작용을 하지 않을 때도 동작한다.
많은 경우에, WorkManager를 이용하는 것이 foreground service를 직접적으로 이용하는 것보다 더 좋다.

 

 

2. Background Service

  • 사용자에게 보이지 않는 작업을 수행한다.
  • 시스템에서 리소스가 부족할 경우 강제 종료 될 수 있다. 
  • API 26(오레오) 앱이 포그라운드에 있지 않을 때 백그라운드 서비스를 강제로 종료시킨다.
    scheduled job을 사용하여 강제 종료를 막을 수 있다.

 

3. Bind Service

  • 클라이언트 - 서버 구조에서 서버에 해당한다.
  • 액티비티와 같은 컴포넌트들이 바인드 한 후 요청을 보내고, 응답을 받고, 프로세스간 통신(IPC)를 할 수 있도록 만들 수 있다.
  • Bound된 서비스는 다른 앱의 컴포넌트가 이 서비스에 바인딩 되어 있는 동안에 실행된다. 
    여러개의 컴포넌트가 동시에 서비스에 바인딩될 수 있는데, 이 컴포넌트들이 바인딩 햊제시 서비스는 destroy된다.

Foreground, Background 서비스는 started Service, Bind 서비스는 bound Service로 작동이 된다. 

( 한 서비스에서 bindService()와 startService()의 실행이 동시에 가능하다.)

 


Service 생명주기

 


Service 구현

 

- AndroidManifest.xml

Service는 어플리케이션 컴포넌트이므로 AndroidManifest.xml 파일에 등록해야 한다.

<manifest ... >
  ...
  <application ... >
      <service android:name=".MyService" />
      ...
  </application>
</manifest>

 

서비스를 자신의 앱에서만 사용할 것이라면 android:exported = false로 지정하면 다른 앱에서 서비스를 사용하지 못한다.

 

서비스를 시작할 때는 명시적 인텐트를 사용해야 한다.
암시적 인텐트를 사용할 경우, 액션에 반응하는 모든 서비스가 잠재적으로 시작될 수 있으며 악의적인 앱이 같은 액션을 사용하여 의도하지 않은 서비스를 시작할 수 있다.
Android 5.0(API Level 21) 이상에서는 bindService()와 함께 암시적 인텐트를 사용하면 예외를 던진다.

 

1) startService()

  • onCreate()
    • Service가 처음 생성되었을 경우 onStartCommand() 및 onBind()가 호출되기 전에 실행된다.
      ( 서비스가 이미 실행중인 경우는 실행되지 않는다)
  •  onStartCommand()
    • 다른 컴포넌트(액티비티)가 서비스를 시작하도록 요청하는 경우에 이 메서드를 실행한다.
    • startService 메서드를 통해 호출한다.
    • 이 메서드가 실행될 시 백그라운드에서 무한히 실행되므로, 반드시 stopSelf() 혹은 stopService()로 서비스를 종료시켜야한다.
    • 바인드 서비스만 이용하는 경우에 이 메서드를 사용하지 않아도 된다.
    • 만약 서비스가 startService()를 통해 실행중일 때, 종료하지 않고 다시 서비스를 시작한다면 onStartCommand()가 계속해서 호출된다.
    • onStartCommand()의 Return값을 살펴보면 다음이 있다.
      • START_NOT_STICKY : 시스템이 서비스의 onStartCommand()를 반환 후에 중단시키면 서비스를 재생성하면 안됨
      • START_STICKY : 시스템이 onStartCommand()를 반환 후 서비스를 중단하는 경우 서비스를 자동으로 다시 생성하고 마지막 인텐트는 전달하지 않는다. 이때 Intent를 null로 반환한다.
        별다른 동작이 없는 음악앱과 같은 곳에 적합.
      • START_REDELIVER_INTENT : 시스템이 onStartCommand()를 반환 후에 서비스를 다시 생성하고 이 서비스에 전달된 마지막 인텐트로 onStartCommand()를 호출시 모든 보류 인텐트가 차례로 전달된다.
        즉시 재개되어야 하는 작업을 수행하는데 파일 다운로드에 적합하다.
      START_STICKY의 경우 필요에 의해 명시적으로 시작되고 중단되는 서비스에 사용되며나머지 둘의 경우에는 전송된 명령을 처리하는 동안에만 서비스가 실행되어야 하는 경우에 사용된다.
  • onDestroy()
    • 서비스가 종료될 때 이 메소드가 반한된다. 서비스에 등록된 리소스들을 정리하는 작업들을 여기서 한다.
  • startService(), stopService()
    • 서비스 실행 및 종료시에 사용한다.

2) BindService

  • onCreate()
  •  onBind()
    • 안드로이드의 구성요소가 서비스에 바인딩하고자 하는 경우, 이 메소드가 호출된다.
    • 이 메소드를 구현할 때에는 클라이언트가 서비스와 통신을 주고 받기 위해 사용할 인터페이스를 제공해야 한다.
    • 이를 위해 IBinder를 반환하면 된다.
    • 이 메서드는 항상 구현해야 하지만, 바인딩을 허용하지 않으려면 null을 반환하면 된다.
  • onRebind()
    • unBind()된 후에 서비스가 다시 실행될 경우 호출 된다.
  • onUnbind()
    • unBindService() 호출시 true가 호출된다.
  • bindService()
    • 서비스에 바인딩하고자 할 때 사용한다.
  • unBindService()
    • 서비스를 언바인딩하고자 할 때 사용한다.

Service connection 콜백 메소드

  •  onServiceConnected()
    • 서비스에 바인딩 되었을 때 호출 된다.
  • onServiceDisconnected()
    • 서비스와 바인드 된 구성요소가 중단되거나 예기치 않게 서비스와 연결이 끊어졌을 때 호출된다.
    • unBind()를 통해 호출되었을 경우에는 호출되지 않는다.

Foreground services

 

Foreground 서비스는 사용자가 알아차릴 수 있는 작업을 수행한다.

Foreground 서비스는 status bar notification을 보여주기에 사용자들은 앱이 foreground에서 작업중이고 시스템의 리소스를 사용중인 것을 안다. 알림은 서비스가 중단되거나 foreground로 부터 서비스가 제거되기 전까지 해제될(사라질) 수 없다.

다음은 foreground 서비스를 이용한 예제이다.

  • foreground 서비스로 음악을 재생하고 있는 음악앱. 알림은 현재 재생되고 있는 노래를 보여준다.
  • foreground 서비스로 사용자가 달리는 것을 기록하는 피트니스 앱. 알림은 사용자가 현재 세션동안 이동한 거리를 보여준다.

권한 요청

Android 9(API Level 28) 이상부터는 foreground 서비스를 사용하기 위해서 FOREGROUND_SERVICE 권한이 필요하다. normal permission으로 단지 선언하기만 하면 된다.

<manifest xmlns:android="http://schemas.android.com/apk/res/android" ...>

    <uses-permission android:name="android.permission.FOREGROUND_SERVICE"/>

    <application ...>
        ...
    </application>
</manifest>

 

Foreground service 시작하기

foreground 서비스를 실행하기 위해서 intent를 생성 후 startForegroundService()를 호출한다. API Level 26 이상에서는 앱이 포그라운드 서비스를 만들어야 하는 경우 startForegroundService()를 호출해야 한다. 그리고 서비스가 만들어지고나서 서비스는 5초 이내에 startForeground()를 호출해야 한다.

val intent = Intent(...) // Build the intent for the service
applicationContext.startForegroundService(intent)

 

그 후 대부분 service의 onStartCommand() 코드 안에서 service가 foreground에서 작동하도록 startForeground()를 호출합니다. 매개변수로는 알림을 식별할 수 있는 값과 알림 객체 자체를 넘기면 된다.

val pendingIntent: PendingIntent =
        Intent(this, ExampleActivity::class.java).let { notificationIntent ->
            PendingIntent.getActivity(this, 0, notificationIntent, 0)
        }

val notification: Notification = Notification.Builder(this, CHANNEL_DEFAULT_IMPORTANCE)
        .setContentTitle(getText(R.string.notification_title))
        .setContentText(getText(R.string.notification_message))
        .setSmallIcon(R.drawable.icon)
        .setContentIntent(pendingIntent)
        .setTicker(getText(R.string.ticker_text))
        .build()

// Notification ID cannot be 0.
startForeground(ONGOING_NOTIFICATION_ID, notification)

 

제한
Android 12 이상의 앱에서는 background있는 동안 foreground 서비스를 실행할 수 없다. 만약 background에 있는 동안 foreground 서비스를 실행한다면 특별한 경우를 제외하고 ForegroundServiceStartNotAllowedException 에러가 발생한다.

 

Foreground 서비스 종료하기

foreground 서비스를 제거하기 위해서 stopForeground()를 호출하면 된다. 이 메서드는 boolean 값을 인자로 가지는데 이는 상태바에서 알림이 제거되었는지를 나타낸다.

만약 서비스를 foreground에서 수행하는 동안 service를 종료한다면, 알림도 사라진다.

 

 

 

- 참고

 

[안드로이드] 컴포넌트 - 서비스개요와 ForegroundService

 

[Android] 컴포넌트 - 서비스(Service)

 

[Android] Service 정리