[Android] Service

유민국·2023년 6월 30일

Service

  • 백그라운드에서 실행되는 작업을 처하기 위한 컴포넌트
  • UI를 가지지 않기 때문에 사용자와 직접 상호작용하지는 않는다
  • 앱간 호출이 가능하다.
  • 사용자가 다른 앱으로 전환하더라도 백그라운드에서 계속해서 실행된다.
  • 서비스를 바인딩 하면 상호작용이 가능해진다 이는 프로세스 간 통신(IPC)도 수행도 가능하게 해준다. 예를 들어 한 서비스는 네트워크 트랜잭션을 처리하고, 음악을 재생하고 파일 I/O를 수행하거나 content provider와 상호작용할 수 있다.

주요 목적

  • 음악 재생
  • 네트워크 요청
  • 백그라운드 위치 추적
  • 알람 처리

언제 Service를 써야할까?

  • 백그라운드에서 오랜 시간 작업이 필요할 때
  • 사용자에게 지속적으로 정보를 제공해야할 때(예: 위치 추적)
  • UI 없이 데이터를 주고받고 싶은 경우

서비스는 스레드를 생성하지 않고 메인 스레드에서 실행되기 때문에, 서비스의 작업이 많다면 성능의 저하가 오거나 '애플리케이션이 응답하지 않습니다(ANR)' 오류(오류가 나면 제거 되나요?)가 날 수 있기 때문에 서비스 내에서 별도의 스레드를 생성하여 사용한다면 위험을 줄일 수 있다.

  • 예를 들어 activity가 실행되는 중에만 음악을 재생하고자 한다면 onCreate()안에 스레드를 생성하고 이를 onStart()에서 실행시키고, onStop()에서 중단하면 된다.
  • 기존의 Thread클래스 대신 AsyncTask또는 HandlerThread를 사용하는 방안도 고려해보자 관련문서

EX) 음악 재생 Foreground Service

class MusicService : Service() {
    override fun onStartCommand(intent: Intent?, flags: Int, startId: Int): Int {
        val notification = NotificationCompat.Builder(this, "channel_id")
            .setContentTitle("재생 중")
            .setContentText("좋은 음악을 듣고 있어요")
            .setSmallIcon(R.drawable.ic_music_note)
            .build()

        startForeground(1, notification)

        // 음악 재생 로직 시작
        return START_STICKY
    }

    override fun onBind(intent: Intent?): IBinder? = null
}

Service의 실행 방식 vs 실행 상태

분류 기준이름설명
실행 방식Started ServicestartService()로 시작되며, 백그라운드 작업 후 스스로 종료됨
Bound ServicebindService()로 시작되며, 클라이언트(Activity 등)가 바인딩을 해제하면 종료됨
실행 상태Foreground Service사용자에게 알림(Notification) 을 통해 보여지며, 시스템에서 강제 종료되지 않음
Background Service알림 없이 동작하며, Android 8.0 이상에선 제약이 많음 (몇 초 내 종료될 수 있음)

Bound Service

  • 다른 컴포넌트가 Service에 바인딩해서 직접 메서드 호출 가능
  • binding된 서비스는 클라이언트-서버 인터페이스를 제공하여 app component와 상호작용(단방향, 양방향 통신)이 가능해진다.
  • 한번에 여러개의 app component를 binding 할 수 있다.

Ex)

val intent = Intent(context, MyService::class.java)
context.bindService(intent, serviceConnection, Context.BIND_AUTO_CREATE)

Service 유형

  • 앱간 호출이 가능한 외부 서비스, 앱 안에서 호출하는 내부 서비스로 나누어 사용 된다.

Foreground

  • Foreground Service는 사용자가 인식할 수 있도록 Notification을 보여주며 실행
  • Ex) 유튜브 뮤직처럼 음악 앱을 실행했을 때 상단 메뉴바에 나타나는 컨트롤 알림도 Foreground Service이다
    • Notification의 채널의 중요도를 낮게 설정해서 방해하지 않게 조용히 알림을 나타내 주는것
  • 메모리가 부족해지거나 절전 모드가 되는 등 다양한 상황에서 안드로이드 OS에 의해 제거 될 수 있고, 이를 방지하고자 할 때는 Foreground로 만들어 사용하도록 한다.

    예를들어 백그라운드에서 음악 플레이어가 실행되고 음악에 대한 정보를 notification을 통해 알려준다. 이 notification은 서비스가 중단되거나 foreground에서 제거되지 않는 이상 안드로이드 OS 시스템에 의해 제거 되지 않는다.

backgorund

  • 요즘은 Background Service는 사용하기 까다롭기 때문에 정말 특정한 상황에서만 사용되며, 대부분의 경우 Foreground Service나 WorkManager로 대체되고 있다
  • background Service는 사용자에게 알릴 필요가 없는 기능들을 수행한다.
  • 앱이 API 레벨 26이상 앱이 포그라운드에 있지 않을 때 시스템에서 백그라운드 서비스 실행에 대한 제한을 적용하고, 백그라운드 처리 가이드를 따라야 한다.

요즘에도 Background Service를 쓸 수 있는 경우는?

  • 앱이 현재 포그라운드에 있는 경우에 잠깐 사용하는 작업
    • 예: Activity 안에서 버튼 누르면 백그라운드로 간단한 작업을 하는 경우
    • 앱이 아직 사용자 눈앞에 있는 동안에만 허용됨

onStartCommand()

  • 시스템에서 activity와 같은 다른 component에서 startService()를 호출하게 되면 이 메소드가 실행되고 서비스가 시작된다.
  • 이 메소드를 구현한 후 서비스를 중단하기 위해 stopSelf()또는 stopService() 메소드를 호출해야 한다.
  • 만약 바인딩만 하고 싶다면 이 메소드를 구현할 필요는 없다.

onBind()

  • 시스템에서 activity와 같은 다른 component에서 bindService()를 호출하게 되면 이 메소드가 실행되고 서비스에 바인딩 된다.
  • 이 메소드를 구현할 때 클라이언트가 IBinder를 반화하여 서비스와 통신하는 데 사용하는 인터페이스를 제공해야 한다.
  • 이 메소드는 항상 override 해야하며, 만약 바인딩을 원치 않는다면 null을 반환하도록 한다.

onCreate()

  • 서비스를 처음 만들 때 onCreate()가 실행되며, Service에 대한 설정을 이 메소드에서 구현하도록 한다.
  • onCreate()onStartCommand() 또는 onBind()를 호출하기 전에 호출되며 이미 서비스가 실행중일 경우에는 onCreate()는 호출되지 않는다.

onDestroy()

  • 서비스가 더 이상 사용되지 않거나 서비스를 제거할 때 onDestroy()가 호출된다.
  • onDestroy()에서는 서비스에서 실행한 Thread, Listener, Reciever등의 리소스를 정리하도록 한다.

Service 생성하기

Manifest

  • 서비스 모두 app의 Manifest 파일에서 선언해야 한다
  • 서비스를 선언하기 위해 <service>요소를 <application> 요소의 하위로 추가하면 된다.
<manifest ... >
  ...
  <application ... >
      <service android:name=".ExampleService" />
      ...
  </application>
</manifest>
  • <service>속성을 추가하여 필요한 권한과 특성을 정의할 수 있다

  • android:name은 필수로 입력해야하며, 서비스의 클래스 이름을 입력한다.

  • 앱의 보안을 위해 Service를 시작할 때는 항상 명시적 intent만 사용해야 한다.

    Android 5.0(API 레벨21)부터 시스템은 개발자가 암시적 intent로 bindService()를 호출하면 예외를 발생시킨다.

  • android:exported="false" 속성을 추가하면 다른 앱에 의해 서비스가 실행되는 걸 막을 수 있다.

  • android:description 속성은 사용자에게 실행중인 서비스에 대한 설명을 제공하도록 하여 사용자가 서비스의 출처를 모르고 중단시키지 않도록 한다.

android docs
내부 bind service 정리글

profile
안녕하세요 😊

0개의 댓글