서비스 컴포넌트는 백그라운드에서 오래 실행되는 작업을 수행하는 것이 목적인 컴포넌트이다. 따로 사용자 인터페이스를 제공하지 않는다. 사용자가 다른 어플리케이션으로 전환하더라고 백그라운드에서 계속 실행된다.
또한 컴포넌트를 서비스에 바인딩하여 서비스와 상호작용할 수 있으며, 프로세스 간 통신(IPC)도 수행할 수 있다. 예를 들어 한 서비스는 네트워크 트랜잭션을 처리하고, 음악을 재생하고 파일 I/O를 수행하거나 콘텐츠 제공자와 상호작용할 수 있으며 이 모든 것을 백그라운드에서 수행할 수 있다.
메인 스레드 이외에 작업을 해야하는 경우에는 반드시 서비스를 써야만하는 것은 아니다. 스레드를 하나 더 만들어서 사용하거나(AsyncTask, HandlerThread), 코루틴을 이용하는 것도 방법이 될 수 있다. 물론 서비스 내부에서 스레드를 또 나누는 것도 가능하다.
포그라운드 서비스는 사용자에게 잘 보이는 작업을 수행한다. 예를 들어 오디오 앱이라면 오디오 트랙을 재생할 때 포그라운드 서비스를 사용한다. 포그라운드 서비스는 알림을 표시해야 합다. 포그라운드 서비스는 사용자가 앱과 상호작용하지 않을 때도 계속 실행된다.
백그라운드 서비스는 사용자에게 직접 보이지 않는 작업을 수행한다. 예컨대 어느 앱이 저장소를 압축하는 데 서비스를 사용했다면 보통 백그라운드 서비스를 이용한 것이다.
안드로이드 컴포넌트가 bindService()를 호출하여 해당 서비스에 바인딩되면 서비스가 바인딩된다.
바인딩된 서비스는 클라이언트-서버 인터페이스를 제공하여 컴포넌트가 서비스와 상호작용하게 하며, 결과를 받을 수도 있고 이와 같은 작업을 여러 프로세스에 걸쳐 프로세스 간 통신(IPC)으로 수행할 수도 있다. 바인딩된 서비스는 또 다른 컴포넌트가가 이에 바인딩되어 있는 경우에만 실행된다. 여러 컴포넌트가 서비스에 한꺼번에 바인딩될 수 있지만, 이 모든 것에서 바인딩이 해제되면 해당 서비스는 소멸된다.
서비스 컴포넌트는 Service 클래스를 상속받아서 작성한다. 서비스에는 다양한 생명주기 함수를 재정의 할 수 있지만, onBind() 는 필수이다.
class MyService: Service() {
override fun onBind(intent: Intent): IBinder? {
return null
}
}
서비스도 컴포넌트 이므로 메니페스트에 등록해야한다.
<service android:name=".MyService"
android:enabled="true"
android:exported="true"/>
서비스를 실행하려면 시스템에 인텐트를 전달해야하는데, 이때 사용하는 함수는 startService() 와 bindService() 2가지이다.
해당 서비스를 인텐트에 담아서 매개변수로 전달해야 한다.
val intent = Intent(this, MyService::class.java)
startService(intent)
// 암시적 인텐트
val intent = Intent("ACTION_OUTER_SERVICE")
intent.setPackage("com.exmple.test_outter")
startService(intent)
stopService(intent)
암시적으로 실행하는 경우, setPackage() 를 해줘야하는데 외부 앱이 백그라운드 상태라면 서비스를 실행할 수 없다. 그리고 서비스를 종료하려면 stopService() 함수로 인텐트를 전달해야 한다.
이 함수로 서비스를 실행하려면 먼저 ServiceConnection 인터페이스를 구현한 객체를 준비해야한다. 이 인터페이스에는 추상함수 onServiceConnected(), onServiceDisconnected() 2개가 정의되어 있다. 각각 이 함수로 서비스를 구동할때, 이 함수로 서비스를 종료할때 자동으로 호출된다.
이렇게 구현한 객체를 bindService() 함수로 인텐트를 시스템에 전달해 서비스를 실행한다. 만약 bindService()로 실행할 서비스가 외부 앱의 것이라면 setPackage() 함수로 패키지명을 명시해야한다.
val connection: ServiceConnection = object: ServiceConnection {
override fun onServiceConnected(name: ComponentName?, service: IBinder?) {}
override fun onServiceDisconnected(name: ComponentName?) {}
}
val intent = Intent(this, MyService::class.java)
bindService(intent, connection, Context.BIND_AUTO_CREATE)
unbindService(connection)
bindService() 함수의 매개변수는 3개이다. 첫번째는 인텐트 객체, 두번째는 ServiceConnection 을 구현한 객체이다. 세 번째 매개변수는 Int 타입의 flags 인데 이 값은 대부분 Context.BIND_AUTO_CREATE 로 지정한다. 이는 서비스가 실행 상태가 아니더라도 객체를 생성해서 실행하라는 의미이다. 만약 Context.BIND_AUTO_CREATE 를 지정하지 않으면 bindService() 함수로 인텐트를 전달해도 서비스가 실행 상태가 아니면 동작하지 않는다. 이렇게 실행한 서비스는 unbindService()로 종료한다.
어떤 함수로 서비스를 실행하느냐에 따라 생명주기가 나뉜다.
startService() -> onCreate() -> onStartCommand() -> 서비스 실행 -> onDestroey() -> 서비스 종료
bindService() ->onCreate() -> onBind() -> 컴포넌트를 서비스에 바인딩 -> onUnbind() -> onDestroy() -> 서비스 종료