본문 바로가기

Mobile/Android

[Android] 안드로이드 서비스 종류 및 생명주기

안드로이드 4대 컴포넌트 - 서비스란?

 

  1.  Service 정의

 

    -  액티비티 처럼 사용자와 상호작용하는 컴포는트가 아닌 백그라운드에서 동작하는 컴포넌트

 

    -  사용자에게 인터페이스를 제공하지 않고, 백그라운드에서 오래 실행되는 작업을 수행할 수 있는 애플리케이션 구성 요소

 

      Ex) 파일 다운로드, 데이터 체크, 데이터 처리, 네트워크 트랜잭션, 음악 재성, Content Provider 와의 상호작용 등

 

    -  액티비티가 종료되어 있는 상태(다른 앱으로 전환)에서도 동작하기 위해 만들어진 컴포넌트

 

      Ex) 화면이 종료된 상태에서도 노래 재생

 

    -  만약, 서비스가 실행되고 있는 상태라면, 안드로이드 OS 에서는 해당 프로세스를 웬만한 경우에 죽이지 않고 관리한자.

 

      메모리 부족 등의 특별한 경우를 제외하고 백그라운드 동작으로 수행하도록 설계됨

 

 

  2.  서비스의 특징

 

    1)  사용자와 상호작용 불가

 

    2)  액티비티의 생명주기에 종속되어 있지 않다.

 

    3)  별도의 스레드에서 동작하지 않는다. 즉 호스팅 프로세스의 '메인 스레드'에서 작동한다.

 

      -  안드로이는 프레임워크단이 Linux로 구현되어 있는 Linux 기반의 프로그램이고, 메모리 관리 또한 Linux Kernel에서 해준다.

 

      -  주의할 점은, 모든 컴포넌트들이 Main Thread(UI 작업을 처리해주는 Thread) 안에서 실행된다는 것

 

    ->  서비스 역시 Main Thread에서 관리하므로, Thread 작업이 필요한 경우에는 작업 Thread 를 생성해서 관리해야 함

 

    ->  그렇지 않으면, ANR(Activity Not Response)가 발생하여 종료되는 문제 발생

 

 

    4)  현재 비활성화된 액티비티보다 우선순위가 높다

 

 

Service 의 종류 - Foreground / Background / Bound

 

  1.  Foreground Service

 

    1)  Foreground Service 는 서비스가 수행하는 동작을 사용자에게 알릴 때 사용한다.

 

      -  따라서, 사용자에게 Notification 을 통해 서비스가 실행되고 있음을 알려야 한다.

 

        Ex) 음악 플레이어의 경우, 현재 실행중인 음악 등의 정보를 Notification 을 통해 알려주고 있다.

 

            이 Notification은 서비스가 중단되거나 Foreground 에서 제거되지 않는 이상 지울 수 없다.

 

      -  대부분의 경우, WorkManager(Jetpack에 있는 API)를 사용하는 것이 Foreground Service 를 직접 사용하는 것 보다 낫다고 한다.

 

 

    2)  Foreground Service 는 활성화된 액티비티와 동일한 우선순위를 가진다.

 

      -  따라서, 시스템에 메모리가 부족하더라고 Android System에 의해 종료될 확률이 낮다.

 

 

  2.  Background Service

 

    1)  사용자에게 보이지 않는 백그라운드 작업 수행

 

    2)  시스템 리소스가 부족할 경우 강제 종료 가능

 

    *  API 26(오레오) 이상부터는 앱이 포그라운드에 있지 않을 때, 백그라운드 서비스를 강제 종료시킨다.

 

   -> 따라서, 즉각적인 실행을 요구하는 작업에는 포그라운드 서비스를, 예약된 작업일 경우에는 알람매니저나 워크 매니저를 상ㅇ해야 함

 

 

  3.  Bound Service

 

    1)  bound service 는 IBinder 라는 인터페이스로 서버-클라이언트 관계처럼 서비스와 상호작용

 

      -  여러 프로세스에서 같은 서비스에 바인딩하여 작업 수행 가능

 

     -> 여러 프로세스에 걸쳐 프로세스 간 통신(IPC, Interprocess communication)을 수행할 수 있음

 

    3)  Bind Service는 여기에 바인딩된 컴포넌트들이 전부 바인딩 해제되면, 서비스가 소멸됨

 

Service 의 형태 - Started / Bound

 

  1.  Started Service

 

    -  startService()를 호출하여 시작

 

    -  서비스가 한 번 시작되면 -> 백그라운드에서 무한정 실행됨

 

  2.  Bound Service

 

    -  bindService()를 호출하여 시작

 

    -  서비스가 액티비티와 연결되어 있는 동안에만 실행되고, 액티비티가 사라지면 서비스도 동시에 소멸된다.

 

 

  3.  한 서비스에서 bindService()와 startService() 를 전부 실행 가능

 

    -  startService()로 무한히 실행하게 하고,

 

    -  bindService()를 통해 앱의 구성요소에서 통신 가능

 

   -> 이 경우, 모든 구성 요소에서 바인드가 해제되더라도 서비스가 종료되지 않음  

 

   -> 반드시 stopService() 혹은 stopSelf()를 통해 서비스를 종료시켜야 함

 

 

Service 생명주기/콜백함수

 

Service 생명주기

 

  -  안드로이드 서비스를 생성하기 위해서는 Service 를 Implemet 해야하고,

 

  -  안드로이드의 LifeCycle을 핸들링 하기 위한 콜백 메소드들을 재정의하고,

 

   +  필요한 경우 바인드를 위한 메소드도 Override 해야함

onStartCommand() - 시스템에서 액티비티와 같이 다른 컴포넌트에서 startService() 를 호출하게 되면 onStartCommand() 메서드가 실행되고 -> 서비스가 시작됨

- onCreate()는 처음 서비스가 만들어질 때 호출되므로, 한 번 실행된 이후에는 onStartCommand()가 호출된다.

- 따라서, 중요한 내용들의 경우 onStartCommand() 에 작성한다.

- 이 메소드를 구현한 후 서비스를 중단하기 위해 stopSelf() 나 stopService() 메소드를 호출해야 함

- 바인딩만 할 경우 해당 메소드를 구현할 필요 X
onBind() - 시스템에서 액티비티와 같이 다른 컴포넌트에서 bindService() 를 호출하게 되면 onBind() 메서드가 실행되고 -> 서비스에 바인딩됨

- 이 메소드의 구현체에서는 IBinder 를 리턴하여 -> 클라이언트와 서비스가 통신할 수 있는 인터페이스를 제공해야 함

- 항상 이 메소드를 구현해야 함 -> 바인딩을 원하지 않으면 null 을 반환하는 식으로라도 구현해야 함
onCreate() - 시스템에서 서비스를 처음 생성할 때 onCreate() 메소드를 호출함

- onStartCommand() 또는 onBind()를 호출하기 전에 호출되며, 서비스가 이미 실행중일 경우 -> 이 메소드는 호출되지 않음

- Service 에 대해 설정할 것이 있는 경우 해당 메소드에 구현해야 함
onDestroy() - 시스템에서는 서비스가 더 이상 사용되지 않거나, 서비스를 제거할 때 이 메서드를 호출

- 서비스가 실행한 스레드, 리스너, 리시버 등의 리소스를 삭제하기 위한 동작이 구현되어야 함

- 서비스가 호출하는 마지막 메소드

 

만약, bindService()를 통해 서비스를 생성했다면,

즉, onStartCommand()를 호출하지 않았으면 바인딩된 서비스가 종료되면 -> 시스템에서 서비스를 제거함

 

안드로이드는, 디바이스에 메모리가 부족할 때 -> 서비스를 종료해서 -> 시스템 디바이스 자원을 회복한다.

서비스가 백그라운드에서 오래 실행될수록 시스템에 의해 서비스가 종료되기 쉽다.

만약, 시스템이 자원을 확보하기 위해 서비스를 죽이면 -> 서비스를 다시 실행할 수 있는 자원이 확보되는 경우 onStartCommand() 를 다시 실행한다.

 

Background Service 생명주기

Background Service Life Cycle

 

  -  StartService()를 통해 최소 서비스 실행 시 onCreate()가 호출된다.

 

  -> 그 이후에는 StartCommand()가 호출된다.

 

  -> 서비스를 종료하기 위해서는 stopService()를 실행하거나 / 서비스 자체에서 stopSelf() 를 실행한다.

 

  -> 그러면, onDestroy()가 호출되어 서비스가 종료된다.

 

startService() 서비스를 실행한다
onCreate() - 서비스가 최초 생성될 때 한 번 호출된다
- 이미 실행 중인 서비스라면, 이 함수는 호출되지 않는다
onStartCommand() - 앱의 다른 구성 요소에서 서비스를 실행하면 onStartCommand() 함수가 호출된다
- 이 함수가 호출되면, 서비스가 시작된 것이며 백그라운드에서 작업을 수행한다. 
onDestroy() 서비스가 소멸될 때 호출된다
stopSelf() 서비스가 스스로 중단한다
stopService() 다른 구성 요소가 서비스를 중단한다

 

 

Bind Service 생명주기

 

Bind Service Life Cycle

 

  -  bindService()를 통해 최초 바인드 할 때 -> onCreate() 실행됨 -> 그 후 onBind()가 호출됨

 

  -  바인드를 해제하기 위해서는 unbindService()를 실행하면 -> onUnBind()가 호출되고 -> onDestroy()가 호출되어 서비스가 종료됨

 

bindService() 서비스에 바인딩 할 때 사용
unbindService() 서비스를 언바인딩 할 때 사용
onBind() 다른 구성 요소가 서비스에 바인딩되면 호출
onRebind() (onUnbind() 함수의 리턴 값이 true 인 경우)
unbind 된 후 서비스가 실행중일 경우 onRebind()가 호출됨
onUnbind() unbindService() 실행 시 onUnbind() 호출
서비스를 호출한 구성 요소
onServiceConnected() 서비스에 바인드 되었을 때 호출됨
onServiceDisConnected() - 서비스를 호스팅하는 프로세스가 중단되거나, 종료되어 예기치 않게 서비스에 연결이 끊어졌을 때 호출됨

- 클라이언트가 언바인딩 할 때는 호출되지 않음

 

 

Intent Service 생명주기

 

  -  비동기 처리에 용이

 

Intent Service Life Cycle

 

  -  기본 스레드와는 별개로, onStartCommand()에 전달된 모든 인텐트 실행을 위한 작업 스레드를 생성함

 

  -> 전달된 인텐트는 작업을 위한 큐에 순차적으로 쌓이고

 

  -> 루퍼에 의해 차례대로 onHandleIntent() 에 전달된다.

 

  -> onHandleIntent()에서 작업이 완료되면 서비스를 종료함

 

  -> 별도의 멀티 스레딩 처리를 고민하지 않아도 된다.

 

  -  다른 함수는 오버라이딩하지 않아도 O / onStartCommand()는 반드시 오버라이딩 + super.onStartCommand()를 호출해야 함

 

onStartCommand() 인텐트를 작업 큐로 보낸 후 onHandleIntent()를 호출함
onHandleIntent() 워커 스레드에 의해 순차적으로 호출되어 필요한 작업 수행

 

서비스 정의하기

 

  1.  서비스의 실행

 

    -  서비스의 실행은 액티비티와 같이 다른 컴포넌트에서 startService() 메소드 호출에 의해 이루어짐

 

    -  서비스는 자신을 실행시킨 컴포넌트와는 독립적인 Life Cycle 을 가짐

 

       백그라운드에서 독립적으로 실행 가능(자신을 실행시킨 컴포넌트가 더 이상 존재하지 않아도!)

 

   -> 자신이 stopSelf()를 호출하거나, 다른 컴포넌트가 stopService()를 호출해서 중단해야 함

 

    -  서비스는 필요한 경우, 사용할 Intent 를 넘겨 받을 수 있음 -> startService() 를 호출하며 Intent를 전달

 

   -> 넘겨 받은 Intent는 onStartCommand() 메소드에서 핸들링 가능

 

 

  2.  서비스 처리

 

    1)  Setvice 의 하위 클래스인 IntentService 클래스는 worker thread 를 활용해서 요청을 하나씩 처리

 

      -  오레오 버전부터 ~ JobIntentService 사용 추천

 

    2)  서비스 작업의 분배를 위해 WorkManager 사용 권장

 

      -  지연된 작업 유형에서, WorkManager를 사용하면 앱이 종료되거나 기기가 다시 시작되더라도 지연될 수 있는 비동기 작업을 쉽게 예약 가능

 

  3.  onStartCommand() 메서드의 리턴(flgs) 타입 3가지 

 

    - Service는 실행 시 startService(Intent Service)를 호출하는데 onStartCommand(Intent intent, int flags, int startId)에 intent로 value를 넘겨줄 수 있다.

 

  -> Intent를 전달하면 단방향 통신 

 

  -> 만약, 서비스에 대한 결과를 반환받고 싶다면 PendingInent를 전송하면 -> 서비스는 broadcast를 사용해서 결과 반환 가능

 

  -  인텐트로 전달한 Extradata들을 확인 할 때는, onStartCommand()로 확인

 

 

    (1)  START_STICKY

 

      -  서비스가 강제 종료되었을 경우, 기존에 intent에 value 값이 설정되어 있다고 하더라도

 

         시스템이 Service 재시작 시 intent 값을 null로 초시화시켜서 재시작

 

    (2)  START_NOT_STICKY

 

      -  강제로 종료 된 서비스가 재시작하지 않음

 

      -  시스템에 의해 강제 종료되어도 괜찮은 작업에 사용

 

    (3)  START_REDELIVER_INTENT

 

      -  Service가 종료되어도 시스템이 다시 Service를 재시작시켜주지만 intent 값을 그대로 유지시켜 준다

 

      -  startService() 호출 시 intent value 값을 사용한 경우라면, 이 flag를 사용해서 리턴 값을 설정해주면 된다. 

 

 

참고

 

  -  https://velog.io/@prayme/Android-Service

  -  https://brunch.co.kr/@mystoryg/93

  -  https://seosh817.tistory.com/115