안드로이드

[Android] 서비스(Service)에 대해 알아보자

코딩하는후운 2022. 7. 5. 00:36
반응형

[Android] 서비스(Service)에 대해 알아보자

# 서비스란?

Service는 백그라운드 작업을 위한 애플리케이션 구성 요소이다.

Activity가 사용자에게 직접 보이는 화면이라면

Service는 뒤에서 수행된다.

 

ex. 음악 재생, 파일 입출력, 네트워크 트랜잭션, 전화 앱을 켜놓지 않은 상태에서도 전화를 받을 수 있는 것은 앱을 화면에서 직접 쓰고 있지 않아도 백그라운드에서 서비스가 돌아가고 있기 때문이다.

 

# 서비스의 특징

  • 유저와 상호작용 할 수 없다.
  • 액티비티의 생명주기에 종속되어 있지 않다.
  • 별도의 스레드에서 동작하지 않는다. 호스팅 프로세스의 "메인 스레드"에서 작동한다.
  • 현재 비활성화된 액티비티보다 우선순위가 높다.

 

# 서비스의  종류

서비스에는 여러 종류가 있습니다.

Started Service와 Bound Service를 나누어 설명하지만, 둘을 같이 쓸 수도 있다.

 

1. 포그라운드 서비스 Foreground Service

활성화된 액티비티와 동급의 우선순위를 가집니다.

그래서 시스템에 메모리가 부족하더라도 Android System에 의해 종료될 확률이 적습니다.

Foreground Service는 상태 바(Status bar)에 알림을 표시해야 합니다.

(멜론같은 음악앱을 이용하면 현재 재생중인 음악이 알림에 뜬다.)

대부분의 경우 WorkManager를 사용하는 것이 Foreground Service를 직접 사용하는 것보다 낫다고 합니다.

2. 시작된 서비스 Started Service

startService()를 호출하여 시작하고, 한번 시작되면 따로 그만하라는 코드가 명시되어있지 않다면 계속적으로 실행됩니다.

Api level 26 이상부터는 여기에서 위치정보에 접근하는 것이 제한되어 있으니 이런 경우에도 역시 WorkManager를 이용하세요.

3. 바인딩된 서비스 Bound Service

앱에서 bindService()를 호출하여 해당 서비스에 바인딩할 수 있게합니다.

Bind Service는 IBinder라는 인터페이스로 클라이언트 - 서버 관계처럼 서비스와 상호작용 할 수 있습니다.

이 같은 작업을 여러 프로세스에 걸쳐 프로세스간 통신(IPC, Interprocess communication)을 수행할 수도 있습니다.

Bind Service는 여기에 바인드된 컴포넌트들이 전부 바인딩이 해제되면, 서비스가 소멸됩니다.

 

 

# 서비스의 선언

Manifest에서 선언해주어야 한다.

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

android:exported="false"를 추가하면 서비스를 본인의 앱에서만 사용 가능하게 할 수 있다.

android:description 특성을 추가해서 서비스에 대한 설명을 기재할 수도 있다.

 

# 서비스 생명 주기(Service LifeCycle)

서비스 생명 주기가 두 가지로 나뉜다. (혼용 가능)

- startService()로 서비스를 실행하거나

- bindService()로 바인딩만 제공하는 경우

 

# 서비스의 기본 콜백 메소드

- onStartCommand()

서비스를 시작하도록 요청하는 메소드

서비스를 실행하려는 곳(예를 들면 Activity)에서 startService()를 써서 호출한다.

 

이 메소드가 실행되면 서비스가 시작되고 백그라운드에서 무한히 실행될 수 있다.

서비스를 중단하는 것은 stopSelf() 또는 stopService()를 써서 호출하면 된다.

 

바인딩만 제공하고자 하는 경우에는 onStartCommand()를 구현하지 않아도 된다.

 

- onBind()

다른 구성 요소와 서비스를 바인딩하려는 경우에 호출한다.

바인딩을 실행하려는 곳(예를들면 Activity)에서 bindService()를 써서 호출된다.

 

구현을 할 때는 Interface를 제공해야 한다.

return으로 IBinder를 반환하면 된다.

 

이 메소드는 항상 구현해야 한다.

구현을 하는데 바인딩을 하지 않으면 return값으로 null을 반환하면 된다.

 

- onUnbind()

서비스가 제공하는 인터페이스에서 모든 클라이언트가 연결해제 되었을때 호출된다.

 

- onRebind()

onUnbind()에서 연결이 끊어졌다는 알림을 받은 후 서비스에 새로운 클라이언트가 연결되었을 때 호출된다.

 

- onCreate()

onStartCommand() 또는 onBind()를 호출하기 전에 호출한다.

 

onStartCommand() 또는 onBind()에서 서비스를 실행하거나 바인딩을 하고 나면 서비스가 계속해서 실행되고 있는중이 되기 때문에

일회성으로 실행되며, 서비스가 이미 실행중이면 onCreate()는 호출되지 않는다.

 

4. onDestroy()

서비스를 소멸시킬 때 호출한다.

각종 리소스를 정리하기 위해 구현해야 한다.(쓰레드나, 등록된 리스너, 리시버들을 정리)

 

 

# 서비스 소멸

안드로이드 시스템이 서비스를 강제로 소멸하는 경우는, 메모리 부족으로 인해 리소스 정리하는 경우에 국한 된다.

 

서비스가 강제로 소멸할 가능성은 다음과 같다.

- 서비스가 포그라운드에서 실행되는 경우 : 소멸 가능성 희박함

- 서비스가 바인딩된 경우 : 소멸 가능성 적음

- 서비스가 백그라운드에서 장시간 실행 중 : 소멸 가능성 높음

 

# 서비스 소멸 후 재시작

서비스가 소멸된 경우, 서비스가 다시 시작할 여부는 onStartCommand()의 return값에 따라 달라질 수 있다.

 

1. START_REDELIVER_INTENT

시스템이 서비스를 중단하면 서비스를 다시 생성한다.

그리고 이 서비스에 전달된 마지막 인텐트로 onStartCommand()를 호출한다.

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

(파일 다운로드와 같은 서비스에 적합)

 

2. START_STICKY

시스템이 서비스를 중단하면 서비스를 다시 생성한다.

마지막 인텐트를 전달하지 않고 null 인텐트로 onStartCommand()를 호출한다.

명령을 실행하진 않지만 작업을 기다리는 미디어 플레이어와 같은 서비스에 적합하다.

 

3. START_NOT_STICKY

강제로 종료 된 Service가 재시작 하지 않습니다. 

 

 

# Oreo 버전 백그라운드 제한 개요

앱이 백그라운드에서 실행될 때마다 디바이스의 리소스(예: RAM, 배터리)를 사용한다.

이는 사용자들에게 좋지 못한 경험을 제공.

이로 인해 배터리 수명 저하, 디바이스 성능 저하가 발생할 수 있다.

 

동시에 실행되는 앱이 많을수록 시스템에 많은 부하가 걸림.

이런 문제가 발생할 가능성을 줄이기 위해 Android Oreo버전에서는 사용자가 앱과 직접적으로 상호 작용하지 않을 때 이 앱이 수행할 수 있는 작업을 제한한다.

 

1. 백그라운드 서비스 제한 : 앱이 유휴 상태인 경우 백그라운드 서비스의 사용이 제한된다. 이 기능은 사용자에게 잘 보이는 포그라운드 서비스에는 적용되지 않는다.

 

2. 브로드캐스트 제한 : 앱은 더 이상 명시적 브로드캐스트를 제외한 리시버를 AndroidManifest.xml에 등록할 수 없습니다. 암시적 브로드캐스트 리시버는 런타임에 Context.registerReceiver()를 통해서만 등록이 가능합니다. 
단, 서명 권한이 요구되는 브로드캐스트는 암시적 브로드캐스트 제한에서 제외됩니다. 이는 동일한 인증서로 서명된 앱으로만 브로드캐스트가 전송되기 때문입니다.

 

 

Android 8.0에서는 좀 복잡하며 시스템은 백그라운드 앱이 Background 서비스를 생성하는 것을 허용하지 않는다.

이 때문에 Android 8.0에서는 새 서비스를 Foreground에서 시작하는 새로운 메소드 Context.startForegroundService()를 소개한다.

 

시스템이 서비스를 생성한 후, 앱은 5초 이내에 해당 서비스의 startForeground() 메소드를 호출하여 새 서비스의 알림을 사용자에게 표시해야 한다. 앱이 이 시간 내에 startForeground() 를 호출하지 않으면 시스템이 서비스를 중단하고 앱을 ANR로 선언하게 된다.

 

# 사례와 해결 방법

https://developer.android.com/guide/background?hl=ko 

 

백그라운드 처리 가이드  |  Android 개발자  |  Android Developers

백그라운드 처리 가이드 백그라운드 데이터 처리는 사용자의 기대에 부응하고 사용자에게 도움이 되는 Android 애플리케이션을 개발하는 데 있어 중요한 부분입니다. 이 가이드에서는 백그라운

developer.android.com

 

# Foreground Service

포그라운드 서비스는 사용자에게 눈에 띄는 작업을 수행합니다.

 

포그라운드 서비스는 사용자가 앱과 상호 작용하지 않는 경우에도 계속 실행됩니다. 이는 서비스가 중지되거나 포그라운드에서 제거되지 않는 한 알림을 해제할 수 없음을 의미합니다.

또한, 포그라운드 서비스는 활성화된 액티비티와 동급의 우선순위를 가집니다. 그래서 메모리가 부족하더라도 안드로이드 시스템 의해 종료될 확률이 적습니다.

 

그럼 우린 왜 이 사용자에게 눈에 띄는 작업을 정의해서 포그라운드 서비스로 사용해야하는 걸까?

서비스는 백그라운드에서 실행되며, 백그라운드에서 위치, 카메라 등의 자원을 소비할 수 있습니다.

UI가 없기 때문에 사용자는 앱에서 실행 중인 서비스 유형과 사용 중인 리소스를 인식하지 못합니다. 이는 보안과 성능 모두에 영향을 미칩니다.

 

 

# 구현 방법

1단계

Android 9(API 레벨 28) 이상을 대상으로 하는 앱에 대해 포그라운드 서비스 권하는 요청해야합니다.

<uses-permission android:name="android.permission.FOREGROUND_SERVICE"/>

참고: 이 권한이 없으면 런타임 시 앱이 죽습니다 SecurityException

java.lang.SecurityException: Permission Denial: startForeground from pid=xxx, uid=xxxx requires android.permission.FOREGROUND_SERVICE

2단계

Service클래스 만들기

class SampleForegroundService : Service() {

3단계

사용하는 측에서 service실행

 

4단계

현재 실행되고 있다는 것을 시스템의 status bar에 표시를 해야하기 때문에, notifiaction을 추가로 구현합니다.

 

포그라운드를 시작하려면 startForeground()를 호출하고, 중지하려면 stopForeground()를 호출합니다.

 

기타, foreground는 정확하게 무엇인가?

정확하게 안드로이드에서 foreground가 뭔지 묻는 질문에 아래에 3개 중에 하나라도 해당되면 포그라운드에 해당된다고 합니다.

  • 액티비티가 시작되었거나 일시 중지되었는지 여부에 관계없이 눈에 보이는 활동이 있다.
  • 포그라운드 서비스가 있다.
  • 다른 포그라운드 앱은 해당 서비스 중 하나에 바인딩하거나 content provider 중 하나를 사용하여 앱에 연결된다.

 

 

참조 :

https://jhshjs.tistory.com/48

https://jizard.tistory.com/216

https://jizard.tistory.com/217

https://woovictory.github.io/2019/05/12/Android-Background-Policy2/

https://junghun0.github.io/2019/05/21/android-broadcastreceiver/

https://velog.io/@woga1999/Android-Foreground-Service-%ED%8F%AC%EA%B7%B8%EB%9D%BC%EC%9A%B4%EB%93%9C-%EC%84%9C%EB%B9%84%EC%8A%A4

반응형