Android/Android 기초

[Android] 안드로이드 Binder(IPC) 구현하기

태크민 2025. 2. 25. 18:40

이전 포스팅(https://jtm0609.tistory.com/297)에서 안드로이드에서 프로세스 간 통신을 위해 IPC를 사용하고, 바인더(Binder)를 통해 이 메커니즘을 적용한다고 설명했습니다.

실제로 우리가 자주 사용하는 Intent, Messenger, ContentProvider 등은 실제로 Android의 추상화된 상위 수준의 IPC로 Binder를 기반으로 구축되어 있습니다.

 

그렇다면 이러한 IPC 방식은 어떤 상황에서 유용하게 구현해서 사용할 수있을까요?

일반적으로는 Intent를 통한 통신으로도 충분하지만,

서비스와 액티비티 간 통신과 같이 Binder(IPC)를 구현하여 통신을 주고 받는 상황이 필요할 수 있습니다.

안드로이드에서 제공하는 대표적인 세 가지 방법은 BroadCastReceiver, Messenger, AIDL등이 있습니다.

각각의 차이와 특성에 대해 알아보도록 하겠습니다.

 


Marshalling(마샬링)

각 차이를 알기 위해 먼저 마샬링이라는 용어를 알아야합니다.

안드로이드 프로그래밍에서 마샬링(marshalling)이란 다양한 구성 요소 간 데이터를 전송할 수 있도록 데이터를 포맷 하는 과정을 말합니다.

 

안드로이드는 다양한 Component(Activity, Service, ContentProvider etc)로 이루어져 있으며, 이러한 Component들이 상호 작용할 때 마샬링은 매우 중요한 역할을 합니다.
특히, 안드로이드 시스템에서는 프로세스 간 통신(IPC)을 할 때 마샬링은 필수적입니다.

  • Marshalling
    바인더 트랜잭션(BC_TRANSACTION)에 포함할 목적으로 상위 레벨 애플리케이션 데이터 구조를 패킷으로 변환하는 절차이다.
  • Unmarshalling
    바인더 트랜잭션을 통해 수신된 데이터(BR_TRANSACTION)에서 상위 수준의 애플리케이션 데이터 구조를 재구성하는 절차이다.

마샬링이라고 하니까 어색할 수 있는데 마샬링은 데이터를 저장하거나 전송할 목적으로 데이터 구조 또는 개체를 한 표현에서 다른 표현으로 변환하는 프로세스를 말한다고 생각하면 되며 마샬링의 한 종류에는 직렬화 / 역직렬화 역시 이에 포함되는 개념입니다.

 


Binder 구현법, Bound Service의 구현 종류

BroadCastReceiver

 

BroadcastReceiver는 publish-subscribe Design Pattern와 같이
등록된 Receiver들에 대해 특정 이벤트에 대한 Broadcast를 보내 IPC를 구현하는 방법입니다.

이벤트 예시는 아래와 같습니다.

  • 시스템이 부팅되었을 때
  • 디바이스가 충전을 시작했을 때
  • 새로운 데이터가 다운로드 됐다거나 하는 상황 등에 이용된다.

BroadcastReceiver 비동기로 동작되며 Event를 전달받은 onReceive()는 UI Thread에서 처리되게 됩니다.

기본적으로 Android OS를 통한 1:多 단방향(one-way) 통신이라는 특징이 있습니다.

 


Messagner

 

Messenger는 객체 생성시 파라미터로 전달한 Handler를 통해 MessageQueueMessage를 전달함으로써 IPC를 구현하는 방법입니다.
사실 Handler의 경우 한 Process 내 Thread들간의 통신 역할을 담당하고있지만, Messenger의 경우 Parcelable을 상속받고 있고 전달받은 Handler를 wrapping 함으로써 해당 Local Handler를 다른 프로세스에서 이용 할 수 있는 환경을 조성하고 있습니다.

/**
 * Reference to a Handler, which others can use to send messages to it.
 * This allows for the implementation of message-based communication across
 * processes, by creating a Messenger pointing to a Handler in one process,
 * and handing that Messenger to another process.
 *
 */
public final class Messenger implements Parcelable {
    private final IMessenger mTarget;
    /**
     * Create a new Messenger pointing to the given Handler.  Any Message
     * objects sent through this Messenger will appear in the Handler as if
     * {@link Handler#sendMessage(Message) Handler.sendMessage(Message)} had
     * been called directly.
     * 
     * @param target The Handler that will receive sent messages.
     */
    public Messenger(Handler target) {
        mTarget = target.getIMessenger();
    }
    ...
}

 

Messenger를 통해 원격 프로세스에서 보낸 Message는 MessageQueue를 통해 다른 Process의 Local Handler로 전달됩니다.
그리고 MessageQueue는 Single Thread에서 한번에 하나의 Message를 처리하기에 Thread-Safe하며 비동기로 동작됩니다.

또한, Message는 replyTo property를 통해 Message를 보낸 Messenger에 대한 instance를 얻을 수 있습니다.

이는 Server가 Client의 Message를 받고 reply할 수 있는 two-way communication 환경을 조성해줍니다.

/**
 * Defines a message containing a description and arbitrary data object that can be sent to a Handler. 
 * This object contains two extra int fields and an extra object field that allow you to not do allocations in many cases.
 * 
 * While the constructor of Message is public, 
 * the best way to get one of these is to call Message.obtain() 
 * or one of the Handler.obtainMessage() methods, which will pull them from a pool of recycled objects.
**/
public final class Message implements Parcelable {
    public int what;
    public int arg1;
    public int arg2;
    public Object obj;
    
    /**
     * Optional Messenger where replies to this message can be sent.
     * The semantics of exactly how this is used are up to the sender and receiver.
     */
    public Messenger replyTo;
    ...
}

 


AIDL (Android Interface Definition Language)

AIDL(Android Interface Definition Language)은 Client와 Service가 모두 동의한 프로그래밍 Interface를 정의하여 해당 Interface를 통해 1:1 Process 간 통신(IPC)을 구현하는 방법입니다.

 

[ IRemoteService.aidl ]

package com.example.android

interface IRemoteService {
    /** 
     * Request a calculatation to the Service. 
     */
    int calculate(a:Int, b:Int, c: Int);
}

 

마샬링 방식

앱이 다른 프로세스(예: 백그라운드 서비스)와 데이터를 주고받을 때, 정보를 변환하는 과정이 필요합니다. 이를 마샬링(Marshaling)이라고 했습니다.

앞서 설명한 BroadcastReceiver Messenger 의 경우 원격 process에서 전달한 data을 이용하기 위해선 Marshaling을 해줘야하지만 AIDL을 이용할 경우, 공통된 interface에서 주고 받을 data type을 토대로 Android OS가 Marshaling 작업을 대신 수행해주고 있기 때문에 App단에선 더욱 깔끔하고 명확한 RPC 통신을 구현할 수 있습니다.

 

 

 

1. BroadcastReceiver & Messenger 방식

  • BroadcastReceiver: 데이터를 Intent에 담아서 주고받음
  • Messenger: 데이터를 Message 객체에 담아서 주고받음
  • 둘 다 데이터를 주고 받을 때 Parcelable을 사용해 직접 변환(마샬링)해야 함
    → 즉, 개발자가 직접 데이터를 포맷에 맞춰 변환하는 작업을 해야 함

2. AIDL 방식

  • AIDL은 공통된 인터페이스를 미리 정의하고, Android OS가 자동으로 데이터를 변환해 줌
  • 개발자가 직접 Parcelable을 만들고 변환하는 번거로움이 없음
  • 복잡한 데이터 구조도 쉽게 주고받을 수 있음
    → 더 깔끔하고 관리하기 쉬운 코드 작성 가능

멀티 스레딩 처리

AIDL은 Messenger와 같이 1:1 Process 통신(IPC)을 지원하는 방법이지만, 멀티 스레드 처리 방식에서 차이가 있습니다. AIDL은 멀티스레드 환경에서 동작하며, 여러 클라이언트의 요청을 동시에 처리할 수 있습니다.

반면, Messenger는 Handler 기반으로 동작하여 단일 스레드에서 요청을 순차적으로 처리합니다. 따라서 Messenger는 요청 순서를 보장하지만, 하나의 요청이 끝나야 다음 요청을 처리할 수 있어 응답 속도가 느려지는 단점이 있습니다.

 

AIDL은 Direct Call 방식으로 동작하기 때문에 여러 클라이언트가 동시에 호출할 경우 요청 순서를 보장할 수 없습니다. 따라서 개발자는 멀티스레드 환경에서 발생할 수 있는 동기화 문제를 고려하여 Thread-safe하게 구현해야 합니다.

 

동기  처리 방식

AIDL은 정의된 API의 return Type을 통해 two-way communication을 지원하지만 (messenger와 동일), 기본적으로 동기(Sync)로 실행됩니다.

RPC 호출이 동기적이여서 오래 걸리는 작업이라면 메인쓰레드에서 실행된다면 ANR이 발생할 수 있습니다. 따라서 별도의 쓰레드 관리가 필요할 수도 있습니다.

하지만 만약, aidl call을 비동기적으로 진행하고 싶은 경우엔 aidl method keyword에 oneway 를 붙여주고 return type은 void로 지정하면 됩니다.

 

[IRemoteService.aidl]

package com.example.android

interface IRemoteService {
    /** 
     * Request initialization to the Service. 
     */
    oneway void initialize();

    /** 
     * Request a calculatation to the Service. 
     */
    int calculate(a:Int, b:Int, c: Int);
}

 

혹은, 위와 같은 비동기 작업에대한 결과를 Client가 알아야 할 경우 AIDL Callback을 함께 정의함으로써
위의 방법과 마찬가지로 비동기식 RPC를 구현할 수 있습니다.

 

[IRemoteService.aidl]

package com.example.android

import com.example.android.IRemoteServiceCallback;

interface IRemoteService {
    /** 
     * Request initialization to the Service. 
     */
    oneway void initialize(IRemoteServiceCallback callback);

    /** 
     * Request a calculatation to the Service. 
     */
    int calculate(a:Int, b:Int, c: Int);
}

 

[IRemoteServiceCallback.aidl]

package com.example.android;

oneway interface IRemoteServiceCallback {
    /** 
     * Notify the Service is initialized. 
     */
	void onInitialized();
}

어떤걸 쓰면 좋을까요?

각각의 방식의 특징과 차이점은 분명하기에 마주하는 상황에 맞춰 IPC를 구현하면 될 것 같습니다.

  • 통신 방향성 (Two-way vs One-way)
  • 동시성을 요구 (싱글 스레드 vs 멀티스레드)
  • 복잡한 Object 데이터 처리 (마샬링 방식 차이)

 


마무리

이렇게 이번 포스팅에선 IPC를 위해 Android에서 제공하는 3가지 방식에 대해 알아봤습니다.

  • BroadcaseReceiver - one-way 비동기 IPC 구현
  • Messenger - two-way 비동기 IPC 구현
  • AIDL - 명확한 API를 통한 two-way 동기/비동기 IPC 구현


참고자료

https://velog.io/@alsgus92/Android-IPC%EB%A5%BC-%EC%9C%84%ED%95%9C-3%EA%B0%80%EC%A7%80-%EB%B0%A9%EB%B2%95-%EC%86%8C%EA%B0%9C-%EC%9D%B4-%EC%A4%91%EC%97%90%EC%84%9C-AIDL%EC%9D%84-%ED%8C%8C%ED%97%A4%EC%B3%90%EB%B3%B4%EC%9E%90-1

 

[Android] IPC를 위한 3가지 방법 소개, 각 방법의 특징과 차이점은?

App을 개발하다 보면, 서로 다른 Process와 통신해야 할 필요가 있다. 특히, Middle-Ware 성격의 App을 개발하다 보면 IPC의 중요성과 필요성은 더욱 커지게 되는데 Android는 이러한 IPC mechanism을 위해 아래

velog.io

https://velog.io/@tjeong/Android-%EB%B0%94%EC%9D%B8%EB%94%A9-%EC%84%9C%EB%B9%84%EC%8A%A4

 

Android 바인드 서비스

바인드 서비스 바인드된 서비스란 클라이언트-서버 인터페이스 안의 서버 역할을 맡은 컴포넌트(서비스)를 말한다. 이를 사용하면 컴포넌트(Activity 등)를 서비스에 바인딩하고, 요청을 보내고,

velog.io

https://medium.com/@wodbs135/%EC%9D%98%EB%8F%84-intent-%EB%A5%BC-%EC%95%8C%EA%B3%A0-%EC%82%AC%EC%9A%A9%ED%95%98%EA%B8%B0-d724e9d938d7

 

의도(Intent)를 알고 사용하기

Android IPC mechanism and how we can use it

medium.com

https://medium.com/mj-studio/%EC%95%88%EB%93%9C%EB%A1%9C%EC%9D%B4%EB%93%9C-%EC%96%B4%EB%94%94%EA%B9%8C%EC%A7%80-%EC%95%84%EC%84%B8%EC%9A%94-2-2-bound-service-ipc-87237c4a38ca

 

안드로이드, 어디까지 아세요 [2.2] — Bound Service, IPC

Android Bound Service의 개념과 IBinder, Messenger, AIDL을 이용한 구현

medium.com