13 minute read

이번주 부터는 안드로이드 서비스들에 대하여 공부할 것이다.


1. 안드로이드 서비스 동작 이해

안드로이드 애플리케이션을 개발해본 독자라면 서비스(Service)라는 개념에 대해 한 번쯤은 접해봤을 것이다. 안드로이드에서 서비스는 UI없이 주기적으로 특정한 일을 수행하는 백그라운드 프로세스를 가리킨다. 가령 네트워크를 통해 특정 데이터를 주기적으로 모니터링한다거나 주기적으로 RSS 피드를 수신해서 최신 XML 데이터를 파싱하는 작업처럼 사용자와의 상호작용 없이 다양한 데이터를 백그라운드에서 처리하는 애플리케이션을 작성할 때 이러한 안드로이드 애플리케이션 서비스가 활용된다. 따라서 안드로이드 프로그램을 작성할 때 개발자가 적절한 애플리케이션 서비스를 직접 구현해서 적용한다면 더 반응성이 좋은 애플리케이션을 개발할 수 있다. 안드로이드 프레임워크 또한 애플리케이션 개발에 필요한 중요 API를 시스템 서비스 형태로 지원한다. 예를 들어 현재 단말의 위치를 알아내거나 센서 값을 읽어 오거나, 전화를 거는 등 안드로이드에서 기본적으로 제공하는 기능도 프레임워크에서 서비스 형태로 제공한다.

그럼 안드로이드 서비스에 대해 자세히 알아보기 전에 먼저 예제 애플리케이션을 이용하여 안드로이드 서비스가 어떻게 동작하는지 알아보자.

1.jpg

이 프로그램은 크게 메인 액티비티와 두 개의 시스템 서비스(Alarm Service, Notification Service), 그리고 AlarmService_Service라는 애플리케이션 서비스로 구성돼 있다.

1) 메인 액티비티 상에서 ‘start Alarm Service’ 버튼을 누르면 시스템 서비스인 Alarm Service에 30초마다 사용자가 작성한 애플리케이션 서비스인 AlarmService_Service를 실행해 달라고 요청한다.

2) Alarm Service는 (1)에서 사용자의 요청에 따라 30초마다 AlarmService_Service라는 애플리케이션 서비스를 실행한다.

3) AlarmService_Service는 실행되자마자 시스템 서비스인 Notification Service에 AlarmService_Service 서비스가 시작됐음을 알리는 문자열 출력을 요청한다.

4) Notification Service는 AlarmService_Service에게서 전달받은 문자열을 화면 상단의 상태 표시줄에 출력한다.

5) AlarmService_Service는 시작된 후 15초 후에 종료되며, 종료되면서 종료된 사실을 메인 액티비티에 알리기 위해 토스트(Toast) 메시지를 출력한다.

위 과정들을 통해 애플리케이션에서 시스템 서비스와 애플리케이션 서비스가 어떻게 활용되는지 간단히 알아보았다. 비록 액티비티처럼 UI를 가지고 있지는 않지만 백그라운드로 돌면서 각자의 역할을 제대로 수행하고 있다는 것을 알 수 있다.


2. 안드로이드 서비스 분류

1.png

안드로이드 서비스는 위 그림과 같이 크게 프레임워크에서 기본적으로 제공하는 시스템 서비스와 애플리케이션 개발자가 Service 클래스를 상속해서 구현한 애플리케이션 서비스로 구분할 수 있다. 여기서는 위와 같이 다양한 종류의 안드로이드 서비스에 대해 알아보고, 내부적으로는 서비스가 어떤 방식으로 동작하는지에 관해 간략히 살펴보겠다.


3. 안드로이드 애플리케이션 서비스

애플리케이션 서비스는 안드로이드 SDK의 Service 클래스를 확장한 인스턴스로 UI없이 주기적으로 특정한 일을 수행하는 백그라운드 프로세스를 가리킨다. 이러한 서비스는 액티비티나 브로드캐스트 리시버처럼 안드로이드 애플리케이션 컴포넌트의 일종이다.

애플리케이션 개발자는 서비스를 두 가지 방법으로 이용할 수 있다.

  • 서비스 시작, 종료 : 특정한 기능을 수행하는 서비스를 백그라운드로 실행/종료시킨다.

  • 바인딩을 통한 서비스 원격 제어 : 액티비티처럼 서비스 클라이언트가 서비스에 바인딩을 하게되면 클라이언트는 바인딩이 유지되는 동안 서비스가 제공하는 인터페이스를 통해 서비스의 각종 기능을 제어할 수 있다.

위 두 가지 서비스 이용 방법을 이해하기 위해 미디어 플레이어를 생각해보자. 잘 꾸며진 UI로 구성된 미디어 플레이어 액티비티를 통해 음악을 재생시켰다고 하자. 이때 사용자가 미디어 플레이어를 벗어나서 다른 작업을 하면서도 음악이 계속 재생되게 하려면 음악 재생 기능을 서비스로 만들어야 한다. 이 경우 미디어 플레이어에서 음악을 실행하면 백그라운드 음악 서비스를 시작시켜 음악을 재생하도록 프로그램을 설계해야 할 것이다.

그런데 사용자가 듣던 음악을 일시 멈추고, 되감고, 정지시키고, 다시 재생하는 기능처럼 음악 재생 서비스를 제어해야할 필요가 있을 수 있다. 이때는 실행되고 있는 음악 서비스에 연결해서 해당 서비스를 제어해야 한다. 이렇게 서비스를 원격 제어할 수 있게 서비스에 연결하는 것을 바인딩이라고 한다. 일단 서비스에 바인딩되면 RPC처럼 서비스가 제공하는 메서드를 자유롭게 이용할 수 있으므로, 이를 통해 서비스 동작의 제어가 가능하다.

애플리케이션이 서비스를 생성하려면 startService(), bindService() 같은 API를 상황에 맞게 이용하면 된다. 단순히 백그라운드에서 특정 동작을 하는 서비스를 실행하기만 하면 된다면 startService()를, 서비스에 바인딩해서 서비스가 제공하는 인터페이스를 통해 서비스를 제어하고 싶다면 bindService()를 통해 서비스를 생성하면 된다.

서비스는 안드로이드 애플리케이션 컴포넌트이므로 다음 그림과 같은 생명주기를 가진다. 이 그림에서는 서비스의 시작/종료를 목적으로 하는 startService()와 서비스의 원격 제어가 목적인 bindService()로 서비스를 생성했을 때의 생명주기가 약간 다른 것을 확인할 수 있다.

2.jpg

  • onCreate(): 서비스가 처음 생성될 때 호출되며 일반적으로 서비스를 초기화하는 코드가 포함된다.

  • onDestroy(): 서비스가 종료되기 직전에 호출되는데, 이때 서비스가 사용한 리소스 모두 해제 해야한다.

  • onStartCommand(Intent, int, int): 오직 startService()에 의해 시작된 서비스에서 onCreate 메서드 다음으로 호출된다. startService()의 첫 번째 인자로 넘어온 인텐트가 이 메서드의 첫 번째 인자로 그대로 전달되는데, 이때 인텐트에는 주로 실행할 서비스에 대한 정보 포함된다.

  • onBind(): bindService()에 의해 시작. 클라이언트가 서비스에 바인딩하려고 할 때 호출된다. onBind() 콜백에서는 바인딩할 클라이언트를 위해 해당 서비스와 연결 가능한 객체 제공한다. 이 객체 통하여 서비스를 원격 제어할 수 있다.

3.1 애플리케이션 서비스의 분류

안드로이드는 애플리케이션 서비스를 로컬 서비스와 리모트 서비스로 구분한다. 이를 구분하는 기준은 서비스와 이를 생성한 서비스 클라이언트(보통 액티비티)가 동일한 프로세스에서 동작하고 있는지 여부이다.

2.png

위 그림의 좌측처럼 액티비티가 startService()나 bindService()와 같은 API를 이용해서 서비스를 생성했을 때 생성된 서비스가 자신과 동일한 프로세스에서 실행되는 경우 해당 서비스를 로컬 서비스라고 부른다. 로컬 서비스는 자신을 생성한 애플리케이션 내에서만 사용될 수 있으며 애플리케이션이 종료하면 함께 종료된다.

리모트 서비스는 위 그림 우측처럼 자신을 생성한 액티비티와는 별개의 독립적인 프로세스 위에서 동작하기 때문에 메인 애플리케이션이 종료하더라도 계속 동작한다. 안드로이드 애플리케이션을 구현할 때 액티비티와 같은 애플리케이션의 메인 부분이 종료해도 계속 동작하면서 특정 작업을 처리해야 하는 서비스의 경우에는 리모트 서비스 생성을 고려해야 한다. 하지만 잘못 구현된 리모트 서비스는 프로그램이 종료하더라도 시스템 자원(배터리 등)을 효율적으로 소모할 수 있기 때문에 설계에 신중을 기해야 한다.

로컬 서비스와 리모트 서비스와의 가장 큰 차이는 서비스 제어를 위한 바인딩 방법이다. 앞에서 바인딩은 서비스 클라이언트 프로그램이 서비스를 원격 제어하기 위해 상호 연결하는 과정이라 이야기한 바 있다.

로컬 서비스의 경우는 서비스와 서비스를 이용하는 클라이언트 프로그램이 동일 프로세스에서 동작하기 때문에 로컬 서비스 바인딩은 클라이언트 프로그램이 바인딩할 로컬 서비스의 래퍼런스만 얻으면 된다. 이렇게 바인딩이 이뤄지면 클라이언트는 바인딩을 통해 얻은 서비스의 래퍼런스를 통해 서비스가 제공하는 메서드를 호출할 수 있다.

반면 리모트 서비스의 경우에는 액티비티와 자신이 모두 별개의 프로세스에서 동작하므로 액티비티가 리모트 서비스를 제어하려면 IPC 매커니즘을 이용해야 한다. 이 경우 다음 장에서 배울 바인더 IPC가 사용되는데, 리모트 서비스 바인딩은 이런 바인터 IPC를 수행하기 위한 연결 설정을 의미한다.

참고로 바인더 IPC 통신에서 서비스와 액티비티 사이에 데이터를 주고 받을 때 마샬링/언마샬링 과정을 거쳐야 하는데 이를 위해 AIDL(Android Interface Definition Language, 안드로이드 인터페이스 정의 언어)를 사용할 수 있다. AIDL은 안드로이드 디바이스에서 두 개의 프로세스가 IPC를 사용해서 서로 통신할 수 있는 코드를 작성하는 데 사용되는 IDL 언어이다. AIDL에 대한 자세한 내용은 안드로이드 개발자 사이트를 참조하면 된다.

이제 지금까지 설명한 로컬 서비스와 리모트 서비스의 바인딩 과정이 어떻게 다른지 ApiDemos의 Local Service Binding과 Remote Service Binding 예제를 분석하며 알아볼 것이다. 책에서는 app형태로 존재하는 ApiDemos를 가지고 실행을 하였지만 필자는 소스코드는 찾았지만 해당 소스코드를 실행시키는 것까지는 안드로이드 개발 지식이 부족하여 하지 못하였다. 따라서 일단은 소스코드만 보면서 진행하도록 하겠다.

소스코드는 아래 url를 clone하여 볼 수 있었다.

1
https://github.com/aosp-mirror/platform_development

로컬 서비스

5.jpg

위 그림처럼 앱에서 Bind Service 버튼을 누르면 LocalService가 실행되면서 바인딩된다. 이렇게 바인딩을 하고 나면 Binding 액티비티는 LocalService 서비스가 제공하는 다양한 메서드를 이용할 수 있게 된다.

액티비티가 로컬 서비스와 바인딩되는 방법에 대해 관련 코드를 바탕으로 좀 더 자세히 살펴볼 필요가 있다.

3.jpg

위 그림은 로컬 서비스 바인딩을 위한 액티비티와 서비스의 동작 과정을 순서대로 나타낸 것이다.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
private OnClickListener mBindListener = new OnClickListener() {
            public void onClick(View v) {
                doBindService();
            }
  };


void doBindService() {
            // Attempts to establish a connection with the service.  We use an
            // explicit class name because we want a specific service
            // implementation that we know will be running in our own process
            // (and thus won't be supporting component replacement by other
            // applications).
            if (bindService(new Intent(Binding.this, LocalService.class),
                    mConnection, Context.BIND_AUTO_CREATE)) {
                mShouldUnbind = true;
            } else {
                Log.e("MY_APP_TAG", "Error: The requested service doesn't " +
                        "exist, or this client isn't allowed access to it.");
            }
        }

(1) 위 코드는 ‘Bind Service’ 버튼 이벤트 핸들러의 주요 코드를 보여준다. 액티비티에서 ‘Bind Service’ 버튼을 클릭하면 doBindService()가 호출되며 이 메서드에서 내부적으로 bindService() API를 사용해서 LocalService 바인딩을 시도한다.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
    @Override
    public IBinder onIntent intent) {
        return mBinder;
    }

    // This is the object that receives interactions from clients.  See
    // RemoteService for a more complete example.
    private final IBinder mBinder = new LocalBinder();

     /**
     * Class for clients to access.  Because we know this service always
     * runs in the same process as its clients, we don't need to deal with
     * IPC.
     */
    public class LocalBinder extends Binder {
        LocalService getService() {
            return LocalService.this;
        }
    }    

(2) 이제 바인딩할 서비스가 생성됐으므로 안드로이드는 바인딩 처리를 위해 서비스의 onBind() 콜백 메서드를 호출한다. onBind() 메서드는 액티비티가 LocalService 자신과 연결할 수 있게 Binder 클래스를 확장한 LocalBinder 객체를 반환한다.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
        // To invoke the bound service, first make sure that this value
        // is not null.
        private LocalService mBoundService;

        private ServiceConnection mConnection = new ServiceConnection() {
            public void onServiceConnected(ComponentName className, IBinder service) {
                // This is called when the connection with the service has been
                // established, giving us the service object we can use to
                // interact with the service.  Because we have bound to a explicit
                // service that we know is running in our own process, we can
                // cast its IBinder to a concrete class and directly access it.
                mBoundService = ((LocalService.LocalBinder)service).getService();

                // Tell the user about this for our demo.
                Toast.makeText(Binding.this, R.string.local_service_connected,
                        Toast.LENGTH_SHORT).show();
            }

            public void onServiceDisconnected(ComponentName className) {
                // This is called when the connection with the service has been
                // unexpectedly disconnected -- that is, its process crashed.
                // Because it is running in our same process, we should never
                // see this happen.
                mBoundService = null;
                Toast.makeText(Binding.this, R.string.local_service_disconnected,
                        Toast.LENGTH_SHORT).show();
            }
        };

(3) (2)까지의 과정이 정상적으로 진행되어 서비스가 바인딩을 처리할 객체를 제대로 생성했다면 (이 예제에서는 LocalBinder 객체를 의미한다.) 안드로이드 프레임워크는 서비스 클라이언트 측(예제의 경우 Binding 액티비티)의 onServiceConnected(ComponentName, IBinder) 메서드를 호출한다. 이때 이 메서드의 두 번째 인자에는 (2)의 onBinder()에서 생성된 LocalBinder 객체의 레퍼런스가 전달된다. LocalBinder는 IBinder 인터페이스를 상속받은 Binder 클래스를 확장한 것이기 때문에 IBinder 클래스 타입의 service 매개변수로 전달하는 것이 가능하다.

(4) 이제 Binding 액티비티는 LocalBinder 객체의 getService() 메서드를 호출해서 바인딩하려고 했던 LocalService 객체의 레퍼런스 값을 구한다. 이렇게 구한 LocalService 객체의 레퍼런스 값을 액티비티의 mBoundService 멤버 필드에 저장하면 서비스 바인딩이 마무리 된다.

일단 서비스가 바인딩되고 나면 액티비티는 해당 서비스가 가진 모든 메서드와 멤버 필드 값을 mBoundService 멤버 필드를 통해 접근할 수 있다.

리모트 서비스

이번에는 리모트 서비스의 동작 원리와 바인딩에 대해 간단히 알아볼 것이다. 액티비티에서 ‘Bind Service’ 버튼을 누르면 액티비티(프로세스 A)와는 독립적인 프로세스 B에서 RemoteService 서비스가 실행된다.

6.jpg

Remote Service Binding 애플리케이션을 분석하기에 앞서 이를 구성하는 소스 파일에 대해 살펴볼 것이다. 앞에서 설명한 Local Service Binding 예제와는 다르게 액티비티나 서비스 파일 이외에 ISecondary.aidl이라는 AIDL 파일과 이 파일에 의해 자동 생성된 ISecondary.java 파일이 추가된 것을 알 수 있다.

소스 파일명 역할
RemoteService.java RemoteService 서비스 외에 RemoteService를 이용하는 두 액티비티인 Controller와 Binding이 각각 내부 클래스로 선언돼 있다.
ISecondary.aidl 액티비티와 서비스의 통신을 위한 인터페이스 정의
ISecondary.java ISecondary.aidl 파일을 참조해 안드로이드가 자동으로 생성해주는 파일 ISecondary 인터페이스를 기반으로 액티비티와 서비스가 서로 통신할 수 있게끔 마샬링/언마샬링을 수행

Remote Service Binding 애플리케이션을 구성하는 소스 파일

앞서 언급했듯이 안드로이드 SDK는 AIDL 파일에 정의된 인터페이스를 기반으로 리모트 서비스를 바인딩하는데 필요한 기반 코드를 자동으로 생성해준다. Remote Service Binding 예제에서는 ISecondary.aidl를 활용하는데, ISecondary.aidl은 프로세스 ID를 반환하는 getPid() 메서드가 포함된 ISecondary 인터페이스를 정의한다. 따라서 RemoteService를 이용하는 클라이언트 프로그램은 ISecondary 인터페이스를 통해 RemoteService가 제공하는 getPid() 함수를 이용할 수 있게 된다.

1
2
3
interface ISecondary {
    int getPid();
}

물론 위의 aidl 파일에는 소스코드 없이 인터페이스만 정의돼 있다. 실제 aidl에서 정의된 인터페이스를 통한 바인더 IPC를 수행하려면 복잡한 코드를 생성해야 하지만 안드로이드 SDK는 aidl 파일을 참조해서 이를 자동으로 생성해준다. ISecondary.java는 ISecondary.aidl을 통해 자동으로 생성된 주요 코드다. 이 코드의 역할은 서비스 클라이언트와 리모트 서비스 간의 ISecondary 인터페이스에 기반한 바인더 IPC 연결을 설정하는 것이다. 즉, 이 코드를 통해서 RemoteServiceBinding 액티비티는 Remote Service 서비스가 제공하는 ISecondary 인터페이스에 포함된 getPid() 메서드를 마치 해당 클래스에 포함된 메서드를 호출하듯 호출할 수 있다. 하지만 ISecondary.java는 연결만 설정할 뿐이고 getPid()의 실제 코드는 RemoteService 개발자가 직접 구현해야 한다.

이제 본격적으로 리모트 서비스의 바인딩에 대해 알아볼 것이다. 아래 그림은 Remote Service Binding 애플리케이션에서 RemoteServiceBinding 액티비티가 어떻게 RemoteService 서비스에 바인딩하고, 이를 통해 어떻게 RemoteService 서비스가 제공하는 기능을 이용하는지 보여준다.

4.jpg

(1) Binding 액티비티 : RemoteService와의 연결 요청

(2) RemoteService 서비스 : 실제 서비스 메서드 기능 구현 및 서비스와 통신하기 위한 바인더 제공

(3) Binding 액티비티 : 서비스와 바인더 IPC를 수행하기 위한 프록시 객체 생성

(4) Binding 액티비티 : 서비스 프록시 객체를 이용해서 RemoteService 서비스의 getPid() 서비스 프록시 메서드를 호출

(5) 바인더 IPC : 서비스 프록시 객체(ISecondary.Stub.Proxy)에서 서비스 바인더 객체(ISecond.Stub)로 바인더 IPC 데이터 전달

(6) RemoteService 서비스 : RemoteService 서비스의 getPid() 스텁 메서드 호출

위 과정들에 관련해서 소스코드를 포함하여 자세한 설명은 인사이드 안드로이드 책에서 찾아볼 수 있다. 그런데 필자는 읽어도 무슨 소리인지 모르겠다. 솔직히 저 그림을 봐도 왜 Remote Service를 control 하기위해 저렇게 복잡하게 연결을 해놓는지 알 수가 없다. 그래서 자세한 설명은 나중에 기회가 되면 하도록 하겠다.


4. 안드로이드 시스템 서비스

안드로이드의 시스템 서비스는 디바이스 제어, 위치 정보 제공, 알람 설정 및 통지 메시지 표시 등과 같이 시스템의 가장 기본적인 핵심 기능들을 제공한다. 이러한 시스템 서비스는 다음 그림과 같이 안드로이드 프레임워크에서 애플리케이션 프레임워크 레이어 및 라이브러리 레이어에 각각 존재한다.

7.jpg


4.1 시스템 서비스의 분류

네이티브 시스템 서비스

네이티브 시스템 서비스는 C++로 작성돼 있으며 위 그림의 라이브러리 레이어에서 동작한다. 주요 서비스로는 Audio Flinger와 Surface Flinger 등이 있다.

Audio Flinger 서비스

Audio Flinger 서비스는 여러 안드로이드 애플리케이션의 오디오 데이터를 믹싱해서 헤드폰이나 스피커처럼 다양한 오디오 출력 장치로 내보내는 역할을 한다. 안드로이드 장치에서 모든 오디오 데이터는 Audio Flinger를 거쳐 출력된다.

Surface Flinger 서비스

Surface Flinger는 다양한 애플리케이션에서 사용중인 Surface를 조합해 프레임 버퍼 방치로 렌더링해주는 서비스이다.

자바 시스템 서비스

자바 시스템 서비스는 안드로이드 부팅 시 SystemServer라는 시스템 프로세스에 의해 일괄적으로 실행되며, 코어 플랫폼 서비스와 하드웨어 서비스로 나뉜다.

코어 플랫폼 서비스(Core Platform Service)

코어 플랫폼 서비스는 일반적으로 안드로이드 애플리케이션과 직접 상호작용은 하지 않지만 안드로이드 프레임워크가 동작하는 데 필수적인 서비스를 말한다. 주요 서비스는 다음과 같다.

코어 플랫폼 서비스 기능
Activity Manager Service 모든 액티비티에 대한 라이프 사이클 및 액티비티 스택 관리
Window Manager Service Surface Flinger 위에 위치하며, 기기 화면에 그릴 내용을 Surface Flinger로 전달
Package Manager Service apk 파일(안드로이드 패키지 파일)의 정보를 로딩하고, 시스템에 어떤 패키지가 설치되고 로딩돼 있는지에 대한 정보를 제공

하드웨어 서비스(Hardware Service)

저수준 하드웨어 제어를 위한 API를 제공하는 서비스를 말한다. 주요 서비스는 다음과 같다.

하드웨어 서비스 기능
Alarm Manager Service 타이머처럼 특정 시간 후에 애플리케이션을 실행하는 등의 동작을 수행
Connectivity Service 네트워크의 현재 상태에 대한 정보를 제공
Location Service 단말의 현재 위치 정보를 제공
Power Service 장치의 전원 관리를 제어
Sensor Service 안드로이드에 설치된 각종 센서(마그네틱 센서, 가속도 센서 등)의 센싱 값을 제공
Telephony Service 전화기의 상태나 전화 서비스에 대한 정보를 제공
Wifi Service AP 검색이나 연결 리스트 관리 등 무선랜 연결을 제어

자바 시스템 서비스 이용

프레임워크 내부에서나 안드로이드 애플리케이션에서 이러한 자바 시스템 서비스를 이용하려면 각 서비스와 통신 가능한 Local Manager 객체를 이용해야 한다.


5. 시스템 서비스의 실행

안드로이드 애플리케이션에서 애플리케이션 서비스를 실행할 경우 일반적으로 startService() API함수를 사용한다. 그러나 시스템 서비스는 애플리케이션 서비스와 달리 서비스를 직접 실행할 필요가 없이 getSystemService()를 이용해서 바로 이용할 수 있다. 시스템 서비스는 앞에서 언급한 init 프로세스에 의해 안드로이드의 부팅과정에서 미리 실행되기 때문이다.

시스템 서비스는 안드로이드가 부팅할 때 미디어 서버와 시스템 서버라는 두 시스템 프로세스에 의해 실행된다. 미디어 서버 프로세스는 Surface Flinger를 제외한 Audio Flinger, Media Player Service와 같은 네이티브 서비스를 실행하는 역할을 한다. 시스템 서버는 Zygote에 의해 맨 처음 생성되는 자바 기반의 프로세스로 네이티브 시스템 서비스인 Surface Flinger를 비롯한 모든 자바 시스템 서비스를 실행한다.

8.jpg

5.1 미디어 서버 프로세스의 시스템 서비스 실행 과정

9.jpg

5.2 시스템 서버 프로세스의 시스템 서비스 실행 과정

10.jpg

위 과정들은 미디어 서버와 시스템 서버가 안드로이드의 시스템 서비스를 어떻게 실행하는지 간단히 정리한 모식도이다. 그림상으로는 굉장히 간단해 보이지만 전체 코드의 내부적인 동작 방식을 이해하려면 안드로이드 서비스 프레임워크 및 바인더 드라이버에 대해 정확히 알고 있어야 한다.


6. 안드로이드 서비스 프레임워크와 바인더 드라이버 개요

현존하는 모든 안드로이드 시스템 서비스를 분석하는 것은 불가능하다. (애플리케이션 서비스에 대해 자세히 알고 싶은 사람은 안드로이드 애플리케이션 프로그래밍 서적을 참고하면 된다.)

따라서 인사이드 안드로이드 책에서는 시스템 서비스의 작동 원리에 초점을 맞춰서 설명하고 있다. 때문에 앞으로 배울 바인더부터 자바 시스템 서비스 동작 분석까지는 안드로이드 시스템 서비스의 작동 원리를 파악하기 위한 개념(바인더 드라이버와 서비스 프레임워크)과 실제 시스템 서비스(카메라 서비스, 액티비티 매니저 서비스)를 예로 들어 설명하고 있다.

위에서 배운 개념들을 가지고 서비스 사용자가 호출한 foo() 서비스 프록시 함수가 어떻게 바인더 RPC를 통해 Foo 서비스의 실제 foo() 서비스 스텁 함수를 호출하게 되는지에 대해 간단하게 알아볼 것이다.

3.PNG

우선 서비스 사용자는 foo() 프록시 함수를 호출해서 Foo 서비스를 이용하기 위한 인자로 구성된 바인더 RPC 데이터를 전달한다. 바인더 RPC 데이터는 마샬링을 거쳐 서비스 프레임워크를 통해 바인더 IPC 데이터로 생성된 다음, 바인더 드라이버를 통해 서비스 서버 측에 전송된다.

서비스 서버 측에서 수신된 바인더 IPC 데이터는 서비스 프레임워크를 거치면서 언마샬링된 다음, 서비스 스텁의 onTransact() 함수에 전송된다. 마지막으로 서비스 스텁은 해당 바인더 IPC 데이터 안에 포함된 RPC 코드를 통해 Foo 서비스의 foo() 서비스 스텁 함수에 대한 바인더 RPC임을 판단한다. 그러고 나서 수신된 바인더 IPC 데이터에 포함된 바인더 RPC 데이터를 인자로 해서 foo() 서비스 스텁 함수를 호출한다.

지금까지 안드로이드에서 시스템 서비스의 기본 동작 원리에 대해 간단히 살펴보았다. 다음으로는 안드로이드 바인더 IPC에서 바인더 드라이버와 컨텍스트 매니저에 대해 살펴볼 것이다. 그걸 알아본 후에는 서비스 프레임워크와 서비스 프록시 서비스 스텁 등에 관해 알아볼 것이다.


Reference

인사이드 안드로이드

Categories:

Updated: