본문 바로가기
개발/Android

[Android] Service – 1. 기본 사항 (+ 주의할 점)

by Dev Aaron 2020. 11. 8.
반응형

Android 앱 개발자 면접에서 절대 빠질 수 없는 질문이 있습니다. 바로 4대 컴포넌트에 대한 것이죠. 그 중 Service에 대해 이야기하고자 합니다.

우선 “Service”는 개인적으로 제게는 쉽지 않은 주제입니다. 이유는 Service를 다뤄볼 일이 별로 없었기 때문이죠. 사실 Service란 것은 그 자체만 놓고 보면 별게 없습니다. Activity처럼 자체 라이프사이클을 갖고 동작하되 사용자 UI 없이 Background 작업을 위해 제공되는 컴포넌트입니다.

하지만 생각처럼 간단하게 정리할 수 없는 이유가…
백그라운드 처리이기 때문입니다. 백그라운드 처리는 아주 중요하며, 또 쉽지 않습니다. 특히 Android에서는 Android Framework 버전이 올라가면서 백그라운드 처리 작업에 대한 전략이 계속해서 발전해왔습니다.

이를 테면 Lollipop부터는 암시적 인텐트로 Service를 시작할 수 없으며,
Oreo부터는 Foreground Service의 사용법이 달라졌습니다.
거기에 Jetpack에 WorkManager가 추가되면서 백그라운드 작업 구현에 대한 새로운 옵션이 추가되었죠.

단순히 Service라는 것에 대해 이야기하기에는 부족합니다. Android에서 백그라운드 작업을 처리하는 것에 대한 전반적인 이야기가 필요하기 때문입니다. 이야기를 하다보면 결국 WorkManager까지도 가야하죠.

서론이 좀 길어졌는데, 본 포스팅에서는 우선 Service가 무엇이고, 어떤 종류의 Service가 있는지 간단히 살펴보는 것으로 시작하겠습니다. 추후 관련된 포스팅을 이어가도록 하겠습니다.


Service에 대한 내용은 공식 문서에도 아주 잘 설명되어 있습니다. (한글로도 말이죠)
따라서 자세한 내용은 공식 문서를 살펴보시면 되고, 본 포스팅에서는 개인적으로 중요하다고 생각하는 부분만 요약 정리하였습니다.

앞서 이야기한 것처럼 서비스는 백그라운드에서 장시간 동작해야 하는 로직을 구현하기 위해 사용할 수 있는 컴포넌트입니다. 따라서 별도 사용자 UI는 제공하지 않습니다.

서비스에는 다음과 같이 3가지 종류가 있습니다.

  • Foreground Service
  • Background Service
  • Bound Service

주의 사항

각 Service에 대해 이야기하기 앞서 한가지 먼저 언급할 것이 있습니다.
간혹 오해를 할 수 있는 부분인데요. (제가 그랬습니다.)
Service의 정의를 보면 공식 문서에서도 장시간 수행이 필요한 백그라운드 작업을 구현하기 위해 필요한 컴포넌트라고 하고 있습니다.

그런데 여기서 오해하지 말아야 할 것이, Service의 동작 자체는 Main 스레드에서 동작한다는 것입니다. Service 그 자체가 별도 스레드를 생성하여 백그라운드에서 동작하지 않습니다. 그런 이유로 Service에 대해 잘 모른 상태로 구현 시 Service에서 백그라운드 작업 (Network, Disk, DB I/O)을 수행하게 되면 Crash가 발생하는 것을 알 수 있습니다. 혹은 Log를 찍어봐도 별도 스레드가 아니라 Main 스레드에서 동작하는 것을 확인할 수 있습니다. 이와 같은 주의 사항은 이미 구글 공식 문서에서도 언급되어 있으니 참고하시기 바랍니다.

Caution: A service runs in the main thread of its hosting process; the service does not create its own thread and does not run in a separate process unless you specify otherwise. If your service is going to perform any CPU-intensive work or blocking operations, such as MP3 playback or networking, you should create a new thread within the service to complete that work. By using a separate thread, you can reduce the risk of Application Not Responding (ANR) errors, and the application’s main thread can remain dedicated to user interaction with your activities.

https://developer.android.com/guide/components/services

Foreground Service

사용자가 인지할 수 있는 서비스입니다. 인지한다는 것은 눈으로 보고 “지금 어떤 서비스가 돌아가고 있구나”를 인지한다는 것을 의미합니다.

Foreground Service를 시작하려면 startForegroundService() 함수를 호출합니다. (참고로 이 함수는 Oreo 이상부터 존재합니다.)

Background Service

Foreground Service와 다르게 사용자가 인지하지 못하는 서비스로 백그라운드에서 동작합니다. 이와 관련하여 Android 버전 업이 되면서 다양한 전략과 대안들, 변경 사항이 있어왔으며, 이에 대한 내용은 별도 주제로 다룰 필요가 있어 보입니다.

Background Service를 시작하려면 startService() 함수를 호출합니다.

Bound Service

bindService 함수를 통해 컴포넌트를 Service에 바인딩시킬 수 있습니다. 바인딩하게 되면 Client-Server로 동작하게 되어 Service에서 컴포넌트의 요청을 처리할 수 있게 되는 구조가 됩니다. Service에는 여러 컴포넌트가 바인딩할 수 있으며, Service가 종료되는 시점은 바인딩된 모든 컴포넌트가 unbind 되었을 때 종료됩니다.

Bound Service에 바인딩하려면 bindService() 함수를 호출합니다.


여기서 서비스를 또 어렵게(?) 느끼게 만드는 부분이 있습니다. 바로 위에서 언급한 각 서비스들이 완전히 별도의 서비스가 아니라는 점입니다. 다시 말하면 Started Service라 해도 바인딩을 걸어 Bound Service로도 동작이 가능하다는 것입니다.
(Started Service는 startService에 의해 시작된 Service를 의미합니다.)

즉 Started Service로 구현하거나, Bound Service로 구현하거나, 아니면 Started & Bound Service로 구현하거나 모두 가능하다는 것입니다. 각 종류가 별도로도 사용할 수 있지만 또한 동시에 모두 구현 가능하다는 점. 이런 다양성(?)이 서비스를 좀더 복잡하게 느끼게 만드는 부분이라고 생각합니다. (그냥 제가 부족해서 그런 거니 오해하지는 마세요…)

반응형