[4대 컴포넌트] Service(1)

dwjeong·2023년 8월 30일
0

안드로이드

목록 보기
1/28

💡 서비스란

  • 백그라운드에서 오래 실행되는 작업이나 원격 프로세스 작업을 수행하는 구성요소.
  • 사용자 인터페이스 제공하지 않음.
  • 액티비티 같은 다른 컴포넌트가 서비스를 시작할 수 있고, 바인딩하여 상호작용할 수 있음.
  • 프로세스 간 통신 (IPC) 가능.
  • 서비스는 앱과 사용자가 상호작용하지 않아도 백그라운드에서 실행될 수 있는 컴포넌트이므로 필요한 경우에만 생성해야 함.


    예시)
    액티비티와 사용자와의 상호작용 차단 없이 네트워크를 통해 데이터를 가져옴.

1. Started Service

작업이 완료될때까지 계속 실행하도록 시스템에 지시.
백그라운드에서 데이터를 동기화하거나 음악을 재생하는것은 서로 다른 유형의 시작 서비스
(background, Foreground)

2. Bound Service

다른 앱, 시스템이 서비스를 사용하겠다고 할 때 실행됨.
다른 프로세스에 API제공, 시스템은 프로세스 간에 종속성이 있다는 것을 알고 있음.

서비스는 호스팅 프로세스의 메인 스레드에서 실행됨.
따로 지정하지 않는 이상 자체 스레드를 생성하지 않고 별도 프로세스에서 실행되지 않음.



💡 주요 콜백 메소드

1. onStartCommand()

다른 컴포넌트가 서비스 시작을 요청할 때 startService()를 호출하면 이 메소드가 호출됨.
onStartCommand()가 실행되면 백그라운드에서 무기한 실행됨.
따라서 onStartCommand()를 구현하는 경우 작업이 완료되면 stopSelf()stopService()를 호출하여 서비스를 중지해야 함. onBind()를 이용할 경우 구현하지 않아도 됨.

2. onBind()

다른 컴포넌트가 서비스와 바인딩하기 원할 때(ex:RPC 수행) bindService() 를 호출하면 이 메소드가 호출됨. 이 메소드를 구현할 경우 클라이언트 - 서비스 통신 시 사용하는 인터페이스를 제공.
IBinder를 반환해야 함. 항상 구현되어야 하는 메소드이며 바인딩을 허용하지 않을 경우 null을 반환해야 함.

3. onCreate()

서비스가 처음 생성될 때 시스템이 이 메소드를 호출. (onStartCommand() 혹은 onBind() 호출 전에)
서비스가 이미 실행중이라면 onCreate()가 호출되지 않음.

4. onDestroy()

서비스가 더 이상 사용되지 않고 소멸될 때 시스템이 이 메소드 호출.
이때 스레드, 리스너, 리시버와 같은 모든 리소스를 정리해야 함.

컴포넌트가 startService()를 호출하여 서비스를 시작하면 stopSelf()로 스스로 중지하거나 다른 컴포넌트가 stopService()를 호출하여 서비스를 중지할 때까지 계속 실행됨.

컴포넌트가 bindService()를 호출하여 서비스를 생성하고 onStartCommand()가 호출되지 않은 경우, 서비스는 컴포넌트가 바인딩되어 있는 동안에만 실행됨. 서비스가 모든 클라이언트에서 바인딩이 해제되면 시스템은 서비스를 소멸시킴.


💡 Started Sevice 생성하기

Started Service는 다른 컴포넌트가 startService()를 호출하여 시작하고, 그로 인해 onStartCommand() 메소드가 호출되는 서비스.

서비스가 시작되면 서비스를 시작한 컴포넌트와 독립적인 생명주기를 가짐.
따라서 서비스를 시작한 컴포넌트가 파괴되어도 서비스는 백그라운드에서 무기한 실행될 수 있음.

서비스 작업이 완료되면 stopSelf()를 호출하여 스스로 종료하거나 다른 컴포넌트가 stopService()를 호출하여 중지할 수 있음.

액티비티와 같은 컴포넌트는 startService()를 호출하고 서비스에서 사용할 인텐트를 전달하여 서비스를 시작. 서비스는 onStartCommand()에서 이 인텐트를 전달받음.


  • 서비스 클래스 확장하기

onStartCommand()에서 들어오는 모든 호출들을 처리, 핸들러에 작업 개시.
onStartCommand() 메소드는 정수를 반환.

START_NOT_STICKY
onStartCommand()가 반환된 후 시스템이 서비스를 종료하는 경우, 서비스를 다시 생성하지 않는다.
더 이상 필요하지 않은 서비스가 계속 동작하는 것을 방지.

START_STICKY
onStartCommand()가 반환된 후 시스템이 서비스를 종료하는 경우, 서비스를 다시 생성하고 onStartCommand()를 호출하되 마지막 인텐트를 다시 전달하지 않음.
미디어 플레이어 앱에 적합. (혹은 위치 정보 추적 서비스)

START_REDELIVER_INTENT
onStartCommand()가 반환된 후 시스템이 서비스를 종료하는 경우, 서비스를 다시 생성하고 서비스에 전달된 마지막 인텐트로 onStartCommand() 호출. 보류된 모든 인텐트가 차례대로 전달됨.
파일 다운로드와 같은 즉시 재개해야 하는 작업을 수행하는 서비스에 적합.

class HelloService : Service() {

    private var serviceLooper: Looper? = null
    private var serviceHandler: ServiceHandler? = null

    // Handler that receives messages from the thread
    private inner class ServiceHandler(looper: Looper) : Handler(looper) {

        override fun handleMessage(msg: Message) {
            // Normally we would do some work here, like download a file.
            // For our sample, we just sleep for 5 seconds.
            try {
                Thread.sleep(5000)
            } catch (e: InterruptedException) {
                // Restore interrupt status.
                Thread.currentThread().interrupt()
            }

            // Stop the service using the startId, so that we don't stop
            // the service in the middle of handling another job
            stopSelf(msg.arg1)
        }
    }

    override fun onCreate() {
        // Start up the thread running the service.  Note that we create a
        // separate thread because the service normally runs in the process's
        // main thread, which we don't want to block.  We also make it
        // background priority so CPU-intensive work will not disrupt our UI.
        HandlerThread("ServiceStartArguments", Process.THREAD_PRIORITY_BACKGROUND).apply {
            start()

            // Get the HandlerThread's Looper and use it for our Handler
            serviceLooper = looper
            serviceHandler = ServiceHandler(looper)
        }
    }

    override fun onStartCommand(intent: Intent, flags: Int, startId: Int): Int {
        Toast.makeText(this, "service starting", Toast.LENGTH_SHORT).show()

        // For each start request, send a message to start a job and deliver the
        // start ID so we know which request we're stopping when we finish the job
        serviceHandler?.obtainMessage()?.also { msg ->
            msg.arg1 = startId
            serviceHandler?.sendMessage(msg)
        }

        // If we get killed, after returning from here, restart
        return START_STICKY
    }

    override fun onBind(intent: Intent): IBinder? {
        // We don't provide binding, so return null
        return null
    }

    override fun onDestroy() {
        Toast.makeText(this, "service done", Toast.LENGTH_SHORT).show()
    }
}

💡 Started Sevice 중지하기

Started service는 자체 생명 주기를 관리해야 한다.
시스템 메모리를 복구해야하는 경우가 아니라면 시스템은 서비스를 중지하거나 파괴하지 않으며,
onStartCommand()가 반환된 후에도 서비스가 계속 실행됨.

서비스는 stopSelf()를 호출하여 스스로 중지하거나 다른 컴포넌트가 stopService()를 호출하여 중지.

서비스가 onStartCommand()에 대해 여러 요청을 동시에 처리할 경우, 새로운 요청을 받았을 수 있음. (첫 번째 요청이 끝날 때 중지하면 두 번째 요청이 종료됨.)

이 문제를 방지하기 위해 stopSelf(int)를 사용하여 시작요청 ID를 전달.
stopSelf(int)를 호출하기 전에 서비스가 새 시작 요청을 받으면 ID가 일치하지 않아 서비스가 중지되지 않음.

서비스를 제대로 중지하지 않으면 시스템 리소스를 낭비하고 배터리 전원이 소모됨.
서비스에 바인딩을 사용하도록 설정했더라도 onStartCommand() 호출을 받았다면 항상 서비스를 직접 중지해야 함.


💡 Bound Sevice 생성하기

바인딩 서비스는 애플리케이션 컴포넌트가 bindSerivce()를 호출하여 바인딩하는 서비스.
앱의 액티비티 및 기타 컴포넌트에서 서비스와 상호작용하거나 프로세스 간 통신(IPC)을 통해 앱의 일부 기능을 다른 앱에 노출하려는 경우 바인딩된 서비스를 만듦.

바인딩된 서비스를 만들려면 onBind() 콜백 메서드를 구현하여 서비스와의 통신을 위한 인터페이스를 정의하는 IBinder를 반환.

서비스에 바인딩된 컴포넌트가 없으면 시스템이 서비스를 중지.
onStartCommand()를 통해 시작한 서비스처럼 중지할 필요 없음.


💡 서비스 생명주기 콜백

class ExampleService : Service() {
    private var startMode: Int = 0             // indicates how to behave if the service is killed
    private var binder: IBinder? = null        // interface for clients that bind
    private var allowRebind: Boolean = false   // indicates whether onRebind should be used

    override fun onCreate() {
        // The service is being created
    }

    override fun onStartCommand(intent: Intent?, flags: Int, startId: Int): Int {
        // The service is starting, due to a call to startService()
        return startMode
    }

    override fun onBind(intent: Intent): IBinder? {
        // A client is binding to the service with bindService()
        return binder
    }

    override fun onUnbind(intent: Intent): Boolean {
        // All clients have unbound with unbindService()
        return allowRebind
    }

    override fun onRebind(intent: Intent) {
        // A client is binding to the service with bindService(),
        // after onUnbind() has already been called
    }

    override fun onDestroy() {
        // The service is no longer used and is being destroyed
    }
}

Started service가 stopSelf()나 stopService() 호출에 의해 중지되더라도 해당 콜백은 없음.
(onStop() 콜백 없음.)

위의 그림에서 왼쪽은 startService()로 서비스를 생성한 경우,
오른쪽은 bindService()로 서비스를 생성한 경우.

클라이언트가 startService()를 호출하여 onStartCommand()로 시작한 서비스도 클라이언트가 bindService()를 호출할 때 onBind()로 호출을 받을 수 있음.

액티비티와 마찬가지로 onCreate()에서 초기 설정을 하고, onDestroy()에서 모든 리소스 해제.
예) 음악 재생 서비스는 onCreate()에서 재생 스레드를 생성하고 onDestroy()에서 스레드를 중지함.

0개의 댓글