본문 바로가기

안드로이드/Guide

안드로이드 개발 - (4) Android Component: Service

Android Component 2번째로는 서비스(Service)이다. 

 

 

백그라운드에서 오래 실행되는 작업을 수행하며 UI를 제공하지 않는다. 다른 컴포넌트가 서비스를 시작할 수 있으며, 또 다른 앱으로 전환하더라도 백그라운드에서 계속 실행된다. 컴포넌트를 서비스에 바인드하여 서비스와 상호작용할 수 있고, 프로세스간 통신(IPC)도 수행할 수 있다.

 

서비스의 형태는 다음과 같이 Foreground, Background 상태를 갖는다. Foreground의 경우 사용자에게 노출되는 알림(Notification)을 반드시 제공해야 한다.

  • Foreground : Foreground 서비스는 사용자에게 반드시 알림을 제공해야 한다. 사용자가 앱과 상호작용이 있는 한 서비스는 계속 실행된다.
  • Background : Background 서비스는 사용자에게 알림을 보여주지 않으면서 실행된다.

  ※ 앱이 API 레벨 26 이상을 타겟팅하는 경우 앱 자체가 Foreground에 있지 않을 때 시스템이 Background 서비스 실행에 제한을 부여한다. 이와 같이 대부분의 경우 앱에서 예약 된 작업(JobScheduler)을 대신 사용해야 한다

 

  • Bound : 바인드된 서비스는 클라이언트-서버 인터페이스를 제공하여 컴포넌트(Activity ) 서비스에 바인드하거나, 요청을 보내고 응답을 수신하며 심지어는 프로세스간 통신(IPC)까지 수행할 있다. 일반적으로 바인드된 서비스는 다른 애플리케이션 컴포넌트를 도울 때까지만 살고 백그라운드에서 무한히 실행되지 않는다.

  ※ 콜백 메서드 구현여부에 따라 서비스가 시작하거나 바인딩 있다.

 

  • 모든 컴포넌트에서 해당 서비스를 사용할 있고 이를 Intent 시작하면 된다. Manifest에서 서비스를 비공개 선언하면 다른 애플리케이션으로부터의 접근을 차단할 있다.

서비스 생성

  1. Service : 모든 서비스의 기본 클래스이며 상속 받아 구현하는 경우, 서비스의 모든 작업을 수행할 새 쓰레드를 사용하는 것을 추천한다. 서비스는 기본적으로 App의 메인 쓰레드를 사용하기 때문이다.
    • onStartCommand()의 리턴 타입은 Integer, 리턴값은 다음 상수 중 하나여야 한다.
      • 시스템이 서비스를 중단시킨 경우 시스템이 해당 서비스를 계속하는 방법은 나타내는 값

 

상수명

설명

START_NOT_STICKY

서비스 재생성 하지 말 것, 다만 전달할 보류 인텐트(pending intent)가 있는 경우는 예외

애플리케이션이 완료되지 않은 모든 작업을 단순히 재시작 할 수 있을 때 좋음.

START_STICKY

서비스 재생성 후 onStartCommand()를 호출, 마지막 인텐트를 다시 전달하지 말 것.

시스템이 null 인텐트로 onStartCommand()를 호출, 다만 서비스를 시작할 보류 인텐트(pending intent)가 있는 경우는 예외, 이 경우 인텐트가 전달됨.

* 명령을 실행하지는 않지만 무기한으로 실행 중이며 작업을 기다리고 있는 서비스에 적합(ex. 미디어 플레이어)

START_REDELIVER_INTENT

서비스 재생성 후 onStartCommand() 를 마지막 전달된 인텐트로 호출.

모든 보류 인텐트(pending intent)가 차례로 전달된다.

즉시 재개되어야 하는 작업을 능동적으로 수행 중인 서비스에 적합(ex. 파일 다운로드)

 

  • IntentService : Service 클래스의 서브클래스(Android 제공), worker 쓰레드를 사용하여 모든 요청을 처리하되 한번에 하나씩 처리한다. onHandleIntent() 구현하여 사용( 서비스시작 요청에 대한 인텐트를 수신하여 백그라운드 작업을 수행한다.)
    • 서비스가 여러개의 요청을 동시에 처리하지 않아도 되는 경우 사용한다.
    • IntentService 클래스 구조
      • 애플리케이션 메인 쓰레드와 별개로 onStartCommand() 전달된 모든 인텐트를 실행 기존 worker 쓰레드를 생성한다.
      • onhandleIntent() 보낼 인텐트 작업 큐를 생성
      • stopSelf() 콜하지 않아도 요청한 작업이 종료되면 서비스는 중단된다.
      • onBind() 메서드가 null값을 리턴하도록 기본 구현
      • onStartCommand() 메서드가 onHandleIntent() 작업 큐에서 인텐트를 전달하도록 기본 구현
        • 생성자에 super IntnentService(String) 반드시 있어야한다. (worker 쓰레드 이름 / 디버깅)
      • 서비스 시작 : 다른 컴포넌트에서 서비스를 시작하려면 Intent(시작할 서비스 지정) startService() 전달하면 된다. 시스템이 onStartCommand() 메서드를 호출하고 여기에 Intent 전달한다.
      • 서비스가 바인딩을 제공하지 않는 경우, startService() 함께 전달된 인텐트는 애플리케이션 컴포넌트와 서비스 사이의 유일한 통신 방법이다.
      • 다만 서비스에서 결과를 보내고 싶을 때는 서비스를 시작한 클라이언트가 브로드캐스트를 위해 getBroadcast() 사용하여 PendingIntent 만들고 이를 서비스를 시작한 Intent내의 서비스에 전달할 있다. 그러면 서비스가 브로드캐스트를 사용하여 결과를 전달할 있다.
      • 여러 개의 서비스 시작 요청은 서비스의 onStartCommand()에서 상응하는 여러 개의 호출을 한다. 하지만 서비스를 중단할 때는 중단하라는 요청이 하나만 있으면 된다.
      • 서비스 중단 : 시작된 서비스는 자신만의 Lifecycle 직접 관리해야 한다.
      • stopSelf() 또는 stopService() 중단하도록 요청하여 서비스를 중지시킨다.
        • 서비스가 onStartCommand() 대한 여러 요청을 동시에 처리하는 경우, 시작 요청 처리를 완료한 뒤에도 서비스를 중단하면 안된다. 이후 새로운 시작 요청을 받았을 있기 때문이다.( 요청 종료 시에 중단하면 번째 요청이 종료될 있다.)
      • 문제를 피하려면 stopSelf(int startId) 사용하여 서비스를 중단시킬 항상 최신 시작 요청에 기반하도록 해야 한다.(stopSelf(int) 호출 onStartCommand() 전달된 startId 전달한다.)

 

바인드된 서비스(Bound Service) 생성

애플리케이션 컴포넌트가 자신에게 바인드될 있도록 허용하는 서비스로 bindService() 호출하여 오래 지속되는 연결을 생성한다.(일반적으로 startService() 호출하여 서비스가 시작하는 것을 허용하지 않는다.)

onBind() 콜백 메서드를 구현하여 서비스와의 통신을 위한 인터페이스를 정의하는 IBinder 리턴해야 한다.

 

인터페이스를 정의하는 방법

  1. 바인더 클래스 확장
    • 서비스가 본인의 애플리케이션 전용이고, 클라이언트와 동일한 프로세스에서 실행되는 경우 Binder 클래스를 확장하고 인스턴스를 onBind()에서 리턴하는 방식으로 인터페이스 생성한다.
    • 클라이언트가 Binder 받아 사용하고 Binder 구현하며 심지어 Service에서 사용할 있는 공개 메서드에 직접 액세스 있다.
  2. 메신저 사용
    • 터페이스가 여러 프로세스에 걸쳐 적용되도록 해야 하는 경우(IPC), 서비스에 대한 인터페이스를 Messenger 생성할 있다.
    • 서비스가 여러 가지 유형의 Message 객체에 응답하는 Handler 정의한다. Handler Messenger 기초이며, 이를 통해 클라이언트와 IBinder 공유할 있으므로, 클라이언트가 Message 객체를 사용해 서비스에 명령을 보낼 있다.
      • Messenger 모든 요청을 단일 쓰레드로 큐에 저장하여, 서비스를 쓰레드로부터 안전하게 설계하지 않아도 되기 때문이다.
  3. AIDL(Android Interface Definition Language) 사용
    • 객체를 운영 체제가 이해할 있는 원시 유형으로 해체한 다음 여러 프로세스에 걸쳐 마샬링하여 IPC 수행한다. Messenger 사용하는 이전 기법은 사실상 AIDL 기본 구조로 하고 있다. 위에서 언급한 바와 같이 Messenger 단일 쓰레드에 모든 클라이언트 요청 큐를 생성하므로 서비스는 한번에 하나씩 요청을 수신한다. 그러나 서비스가 동시에 여러 요청을 처리하도록 하고 싶은 경우에는 AIDL 직접하용해도 된다. 경우, 서비스가 멀티 쓰레딩을 있어야 하며 쓰레드로부터 안전하게 구축되었어야 한다.
    • AIDL 직접 사용하려면 프로그래밍 인터페이스를 정의하는 .aidl파일을 생성해야 한다. Android SDK 도구는 파일을 사용하여 인터페이스를 구현하고 IPC 처리하는 추상 클래스를 생성하며, 이것을 개발자가 직접 서비스 내에서 확장하면 된다.
      • 마샬링 : 객체의 메모리에서의 표현방식을 저장 또는 전송에 적합한 다른 데이터 형식으로 변환하는 과정

서비스 Lifecycle

 

 

메서드

설명

onCreate()

서비스가 처음 생성되어 일회성 설정 절차를 수행하는 경우 호출됨.(onStartCommand() 또는 onBind() 호출 전)

* 서비스가 이미 실행 중인 경우, 호출되지 않음

onStartCommand()

다른 컴포넌트(ex. Activity)가 서비스를 시작하도록 요청 시(startService()를 호출) 시스템에서 호출됨.

* 바인딩만 제공하고자 하는 경우, 구현하지 않아도 됨.

서비스 작업완료 후 서비스 중단은 stopSelf(), stopService() 호출

onBind()

다른 컴포넌트가 서비스에 바인드되고자 하는 경우 (bindService()를 호출) 시스템에서 호출됨.

구현 시 클라이언트가 서비스와 통신을 주고받기 위해 사용할 인터페이스를 제공(IBinder를 리턴)

* 바인딩을 허용하지 않으려면 null값 리턴

onDestroy()

해당 서비스를 더 이상 사용하지 않고 소멸시킬 때 호출됨.

리소스 정리에 대한 내용 구현(쓰레드, 리스너, 리시버 등)