본문 바로가기
개발/Android

[Android] Service 정리

by 1인용 놀이터 2025. 2. 13.
728x90
반응형

Service 

안드로이드 애플리케이션의 컴포넌트 중 하나로 UI를 제공하지 않아도(사용자와 상호작용 없음) 백그라운드에서 장기 실행되는 작업을 수행한다. 
Service가 시작되면 사용자가 다른 애플리케이션으로 전환 후에도 일정시간 동안 계속 실행될 수 있다. 또한 컴포넌트는 Service에 바인딩하여 Service와 상호작용하고 프로세스 간 통신(IPC)을 실행할 수도 있다.
예를 들어 서비스는 백그라운드에서 네트워크 트랙잭션을 처리하거나, 음악을 재생하거나, 콘텐츠 제공업체와 상호작용 할 수 있다.

 

※ 주의 : 서비스는 자체 스레드를 만들지 않으며, 달리 지정하지 않는 한 별도의 프로세스에서 실행되지 않는다. 애플리케이션 응답 없음(ANR)오류를 방지하려면 서비스 내에서 별도의 스레드에서 차단 작업을 실행해야 한다.

 

서비스 유형

1. Foreground - 포그라운드 Service

포그라운드 서비스는 사용자에게 보이는 작업을 실행한다. 음악 재생을 예로 들어보면, 다른 앱을 실행하고 있어도 그위에 음악을 재생 시킬 수 있다. 포그라운드 서비스는 사용자가 앱과 상호작용하지 않아도 계속 실행 되기 때문에 Notification을 표시해야 한다. 해당 알림은 서비스를 중지하거나 포그라운드에서 삭제하지 않는 한 종료되지 않는다.

또한, Foreground Service는 활성화된 액티비티와 동일한 우선순위를 가지므로 시스템 메모리가 부족하더라도 강제 종료될 확률이 낮다.

 

2. Background Service

백그라운드 서비스는 사용자에게 보이지 않는 작업을 수행한다. 예를 들어 앱에서 스토리지를 압축하는 서비스를 사용하는 경우 일반적으로 백그라운드 서비스이다.

시스템 리소스가 부족할 경우 강제 종료될 수 있으며, API 25 이상부터는 앱이 포그라운드에 있지 않을 때 백그라운드 서비스를 강제로 종료 시킨다.

 

3. Bound Service

서비스는 Activity 등 다른 컴포넌트와 따로 운영 될 수 있지만 바운드 서비스는 다른 컴포넌트들이 서비스와 상호작용하고, 요청을 보내고, 결과를 수신할 수 있도록 한다. 컴포넌트가 bindService()를 호출하여 서비스를 바인딩할 수 있다.

바이드된 서비스는 컴포넌트가포서비스에 바인딩되어 있는 동안만 실행 된다. 여러 개의 컴포넌트가 한 번에 하나의 서비스에 바인딩될 수 있지만, 모든 컴포넌으의 바인딩이 해제되면 서비스가 소멸된다.

 

서비스 기본 사용법 및 LifeCycle

세 가지 종류의 서비스는 2개의 사용법과 2개의 LifeCycle, 독립적으로 실행 되는 started 서비스(포그라운드, 백그라운드) 와 bound 서비스로 구분할 수 있다.

2가지 방법의 차이점은 서비스 호출 메소드와 서비스 안에서 어떤 메소드를 구현하여 사용하느냐에 따라 다르다.

서비스의 라이프사이클은 액티비티보다 훨씬 간단하다. 허나 유저의 눈에 보이지 않기에 더 신경 써 주어야 한다.

1. started 서비스  

서비스를 독립으로 사용할 때 호출 되며, startService() 함수를 이용하며 서비스 안에서 onStartCommand() 메소드에서 구현한다.

 이 서비스는 무한으로 돌기 때분에 사용후 반드시 stop을 해 주어야 한다. stop을 하는 방법은 서비스 스스로 stopSelf()를 콜 하거나 다른 컴포넌트에서 stopService()를 호출해야 한다. 서비스가 스탑되면 시스템에서 서비스를 destroy한다

2. bound 서비스

서비스를 Bound로 사용할 때 호출 되며 bindService() 함수를 이용하며 서비스 안에서 onBind() 메소드에서 구현한다.

이 서비스는 IBinder라는 인터페이스를 제공하며 클라이언트와 소통한다. 다수의 클라이언트가 같은 서비스에 바인들 될 수 있고, 서비스가 바인드된 클라이언트가 더이상 없을 시 시스템에서 destroy한다. 그러므로 굳이 알아서 서비스를 스탑 할 필요가 없다.

 

 

LifeCycle 콜백 구현 예제

activity와 마찬가지로 서비스에도 수명주기 콜백 메소드가 있으며 이를 구현하여 서비스 상태의 변경사항을 모니터링하고 적절한 시점에 작업을 실행할 수 있다.

public class ExampleService extends Service {
    int startMode;       // indicates how to behave if the service is killed
    IBinder binder;      // interface for clients that bind
    boolean allowRebind; // indicates whether onRebind should be used

    @Override
    public void onCreate() {
        // The service is being created
    }
    @Override
    public int onStartCommand(Intent intent, int flags, int startId) {
        // The service is starting, due to a call to startService()
        return startMode;
    }
    @Override
    public IBinder onBind(Intent intent) {
        // A client is binding to the service with bindService()
        return binder;
    }
    @Override
    public boolean onUnbind(Intent intent) {
        // All clients have unbound with unbindService()
        return allowRebind;
    }
    @Override
    public void onRebind(Intent intent) {
        // A client is binding to the service with bindService(),
        // after onUnbind() has already been called
    }
    @Override
    public void onDestroy() {
        // The service is no longer used and is being destroyed
    }
}
  • onStartCommand()

이 함수는 startService() 함수가 호출되면 호출된다. 앞서 말한것 처럼 독립적인 서비스가 필요할 때 구현해야 한다.
startService()로 서비스를 호출했다면 stopSelf() 메소드나 stopService() 함수가 호출될 때 까지 서비스는 계속 살아있다.

  • onBind()

bindService() 함수를 호출할 때 호출된다. Bound 서비스를 사용하고 싶다면 반드시 구현해야 한다.
이 메소드는 클라이언트와 소통할 IBinder를 반드시 리턴해야한다. 만약 binding을 허락하고 싶지 않다면 null을 리턴하자

  • onCreate()

서비스가 생성될 때 한번만 호출된다. 순서는 onStartCommand()와 onBind()보다 앞선다.

  • onDestroy()

서비스가 파괴될 때 호출된다. 이 메소드에서 리스너 및 리시버 제거, 리소스 정리를 해 주어야 한다.

 

 

Manifest에서 Service 선언

Activity 및 기타 컴포넌트와 마찬가지로 애플리케이션의 manifest 파일에서 모든 Service를 선언해야 한다.

Service는 <application> 요소의 하위에 추가한다.

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

 

<service> 속성 중 android:name은 유일한 필수 속성으로, 서비스의 클래스 이름을 지정한다. 애플리케이션을 게시한 후에는 서비스를 시작하거나 바인딩하는 명시적 인텐트에 종속되어 코드가 손상될 위험을 방지하기 위해 해당 이름은 변경하지 않아야 한다.  

 

※ 주의 : 앱을 안전하게 유지하려면 Service를 시작할 때 항상 명시적 인텐트를 사용하고 서비스에 인텐트 필터를 선언하지 않아야 한다. 암시적 인텐트를 사용하여 서비스를 시작하면 어떤 서비스가 인텐트에 응답할지 확신할 수 없고 어떤 서비스가 시작하는지 사용자가 알 수 없으므로 보안 위험이 있다. Android 5.0 (API 수준 21)부터는 암시적 인텐트로 bindService()를 호출하면 시스템에서 예외가 발생한다.

 

andrid:exported 속성의 경우 false로 설정하면 내 앱에서만 서비스를 사용할 수 있고, 이렇게 하면 명시적 인텐트를 사용하는 경우에도 다른 앱이 서비스를 시작하지 못한다.

 

Service 재시작

onStartCommand() 메소드는 반드시 integer를 리턴한다.
이 값은 시스템이 서비스를 죽였을 때, 서비스를 어떻게 재시작 할 지를 나타낸다. 

 

1. START_NOT_STIKY

만약 시스템이 onStartCommand() 값 리턴 이후 서비스를 kill했다면. 서비스를 재시작하지 않는다.
서비스를 재시작하려면 다시 pending intent를 전달해야 한다

 

2. START_STICKY

만약 시스템이 onStartCommand() 값 리턴 이후 서비스를 kill했다면. 서비스를 다시 생성하고 onStartCommand()를 다시 호출한다. 그러나 마지막 intent는 다시 전달해 주지 않는다. 이 값은 보통 미디어 플레이어에 사용된다.(작업을 실행하지 않고 계속 waiting할 때)

 

3. START_REDELIVER_INTENT

만약 시스템이 onStartCommand() 값 리턴 이후 서비스를 kill했다면. 서비스를 다시생성하고 onStartCommand()를 다시 호출하고 마지막 intent까지 전달 해 준다. 서비스가 죽어도 바로 다시 resumed 되야 할 작업을 해야 할 때(예를들면 파일 다운로드) 사용한다.

728x90
반응형