Service

#Android #Service

Android Service Doc

1. Service 종류, 잘 이해가 안되네..

Service는 안드로이드의 4대 컴포넌트중에 하나로, 백그라운드(개념상의 백그라운드이다.)에서 동작하는 기능을 말한다. Service의 종류는 Foreground, Background, Bound 3 종류가 있다.
Foreground Service인데 백그라운드에서 동작한다고? 여기서부터 헷갈리기 시작할 수 있다.

1-1. Service의 백그라운드에서 동작한다는 것은 '직접적으로 드러나지 않은채 동작한다'를 의미한다.

Foreground Service는 유저에게 이 서비스가 작동되고 있다는 것을 인지시킨다는 의미에서 Foreground라는 단어가 붙은 것이라 생각된다. 다만 그 실제 동작하는 바가 독립적인 생명주기를 가지고(동일 프로세스의 Activity 컴포넌트가 Destroy 되어도 Service 컴포넌트는 살아있을 수 있다.) 기저에서 작동하고 있기 때문에(서버를 백엔드라고 하는 것과 같은 느낌으로) 백그라운드에서 동작한다고 소개할 뿐, 실제로는 MainThread에서 실행됨에 주의해야 한다.

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. You should run any blocking operations on a separate thread within the service to avoid Application Not Responding (ANR) errors.

Android Develop, BACKGROUND WORK, Service Overview

그럼 Background Service는 서비스가 작동되고 있다는 것을 인지시키지 않는다는 말인가? 그렇다고 볼 수 있다. 그래서 Background Service는 더이상 사용하지 않는다. 이 서비스를 사용해야하는 대부분의 경우 WorkManager를 이용해서 대체할 수 있다.

1-2. 어떤 Service를 구현할 때 Foreground Service이면서 Bound Service이도록 구현할 수 있어?

그렇게 구현할 수 있다. Service는 Foreground 혹은 Bound 여야 하는 것이 아니라 Foreground 일수도 Bound 일수도 Foreground 이면서 Bound 일수도 있다. (굳이 Foreground+Bound를 쓰는 경우는 많진 않을 것이다.) 그래서 Service Interface는 아래처럼 Foreground Service를 구현하기위해 onBind()나 onUnbind(), onRebind()와 같은 Bound Service 구현을 위한 메서드가 있는 Service 추상 클래스를 상속받는다. (googlesource - Service.java, Android Develop, Reference, Service)

public abstract class Service extends ContextWrapper implements ComponentCallbacks2,
        ContentCaptureManager.ContentCaptureClient {
 
    public void onCreate() {}
    
    public @StartResult int onStartCommand(Intent intent, @StartArgFlags int flags, int startId) {
        //...
    }
    
    public void onDestroy() {}
 
    @Nullable
    public abstract IBinder onBind(Intent intent);

    public boolean onUnbind(Intent intent) {
        //...
    }

    public void onRebind(Intent intent) {}
    

    public final void stopSelf(int startId) {
        //...
    }

    public final void startForeground(int id, Notification notification) {
        //...
    }

    public final void startForeground(int id, @NonNull Notification notification,
            @RequiresPermission @ForegroundServiceType int foregroundServiceType) {
        //...
    }


    public final void stopForeground(@StopForegroundSelector int notificationBehavior) {
        //...
    }

    //...
}

2. Foreground Service

Foreground Service의 생명주기는 onCreate() -> onStartCommand() -> onDestroy() 순이다. (서비스 생명주기 및 플로우 참고 - 안드로이드 서비스(Service), 서준수) 이것을 구현하고 Activity나 Fragment에서 startForegroundService()메서드를 호출하면 Foreground Service가 시작되냐? 이걸로는 부족하다. 한가지를 더 해줘야 한다.

Foreground Service는 위에서도 말했듯이 유저에게 이 서비스가 실행되고 있음을 알려야 한다. Service가 Foreground Service가 되기 위해서는 Service 객체 내에서 startForeground()메서드를 호출해줘야 한다. 이 때, Notification 객체를 만들어서 파라미터로 넘겨줘야 한다. Status Bar에 뜨는 아이콘을 만드는 것이다.

(+ Activity나 Fragment에서 호출하는 startForegroundService() 메서드와 Android API 26 이하에서 사용되는 startService()의 차이점은 startForegroundService() 메서드를 호출하고나서 5초 이내에 startForeground()메서드를 호출해야 한다는 점이라고 한다. 참고 - Foreground Service 사용해보기, kkong93)

(+startForeground() 메서드는 보통 onStartCommand() 메서드 내에서 호출한다. 참고 - Android Develop, Foreground Services, Overview)

참고 포스트


3. Bound Service

Bound Service는 서버-클라이언트 처럼 다른 컴포넌트에 의해 바인딩되어서 통신할 수 있는 Service 이다. (Foreground Service는 호출자와 통신하기위한 명시적인 수단이 없다. Local DB등을 이용하는 우회적인 수단을 사용할 수 있다.) 이 때 클라이언트 컴포넌트와 통신할 수 있도록 통신 객체를 전달하는데 이것이 IBinder 객체이다.

Bound Service는 Foreground Service와 다르게 onCreate() -> onBind()(onRebind()) -> onUnbind() -> onDestroy()의 생명주기를 가진다. bind 되어있는 컴포넌트가 모두 없어지면(모든 컴포넌트가 unbindService()하면 알아서 destroy 시킨다. 서비스 생명주기 및 플로우 참고 - 안드로이드 서비스(Service), 서준수)

3-1. IBinder

Binder 객체를 생성하는 방법은 Binder 클래스 상속, Messenger 객체 구현, AIDL 이용으로 3가지 방법이 있다. 각각에 대한 방법은 안드로이드, 어디까지 아세요 2.2 — Bound Service, IPC 포스트에 자세히 설명되어 있다.

참고 포스트