Service 컴포넌트를 알아보자 - Messenger, AIDL

두리두두·2024년 5월 27일
0

Android

목록 보기
21/25

🔎 IPC란?

  • Inter-Process-Commutication
  • 두 개 이상의 프로세스 간 데이터 교환 및 통신
  • 다양한 컴포넌트 간 효율적으로 데이터를 주고 받는 여러 방법이 있음
  • binder, messenger, intent, content provider 등

🔎 [1] Messenger

  • 프로세스 간 통신 시(IPC) + 외부 앱과 통신 시 Binder 객체 대신 Handler를 매개변수로 하는 Messenger 객체를 사용하는 방법
  • 결국은 얘도 데이터를 주고 받고 , 메소드를 호출하기 위해 사용하는 것
  • Service의 onbind()에서 Messenger.bind 를 리턴하면 된다. 이 때 Messenger 객체를 만들기 위해 핸들러가 필요함.
  • Messenger 객체의 binder(IBinder 타입)을 onbind()에서 반환

Handler

  • 요놈은.. 여기서 가볍게 넘어갈 놈이 아닌듯 하다. 추후 게시글로 쓰겠다.
  • 간단히 말하자면,,, 핸들러는 한 프로세스 내의 여러 쓰레드들끼리 통신할 때 사용하는 객체이다. 그리고 메신저를 통해 핸들러를 여러 프로세스끼리 통신할 때 사용할 수 있다.

Messenger 객체

  • 메신저를 이용해 핸들러를 감싸서 사용
  • 메신저 클래스 주요 멤버 : what(수신자 측에서 무엇에 대한 메세지인지 식별하기 위해), arg1, arg2, obj(수신자에게 보내는 전달 객체) 등

⛓ 사용법 - IPC

  • 서비스를 준비해보자.
  • 메신저 객체 생성하기 전에 핸들러를 준비. 핸들러 안에 handleMessage()에서 액션 지정
import android.app.Service
import android.content.Intent
import android.os.*
import android.util.Log

class MyService : Service() {
	// 메신저 객체
    private lateinit var messenger: Messenger
	// 핸들러 
    inner class IncomingHandler : Handler() {
        override fun handleMessage(msg: Message) {
            // 클라이언트로부터 메시지를 받아 처리
            when (msg.what) {
                MSG_SAY_HELLO -> {
                    Log.d(TAG, "Hello from client!")
                    // 클라이언트에게 응답
                    val reply = Message.obtain(null, MSG_REPLY, 0, 0)
                    msg.replyTo.send(reply)
                }
                else -> super.handleMessage(msg)
            }
        }
    }

    override fun onBind(intent: Intent): IBinder {
        // 메신저 객체 생성 및 핸들러 연결
        messenger = Messenger(IncomingHandler())
        // 메신저 객체의 바인더 반환
        return messenger.binder
    }

    companion object {
        const val MSG_SAY_HELLO = 1
        const val MSG_REPLY = 2
        const val TAG = "MyService"
    }
}
  • 액티비티에서 어떻게 호출하는지 보자
import android.content.ComponentName
import android.content.Context
import android.content.Intent
import android.content.ServiceConnection
import android.os.*
import android.util.Log
import androidx.appcompat.app.AppCompatActivity

class MainActivity : AppCompatActivity() {

    private var messenger: Messenger? = null
    private var bound = false

    // 서비스와 연결될 때 호출되는 콜백 객체
    private val connection = object : ServiceConnection {
        override fun onServiceConnected(name: ComponentName?, service: IBinder?) {
        	// 여기서 service는 onServiceConnected 메서드의 매개변수로 전달된 서비스의 IBinder 객체임!
            // Messenger 객체 생성을 위해 서비스 (IBinder 타입) 넣어줌
            messenger = Messenger(service)
            bound = true
        }

        override fun onServiceDisconnected(name: ComponentName?) {
            messenger = null
            bound = false
        }
    }

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContentView(R.layout.activity_main)
		// bindService()로 호출하는건 똑같다!
        val intent = Intent(this, MyService::class.java)
        bindService(intent, connection, Context.BIND_AUTO_CREATE)
    }

    override fun onDestroy() {
        super.onDestroy()
        if (bound) {
            unbindService(connection)
            bound = false
        }
    }
	
    // 메신저 사용준비 완료, 이 함수 액티비티에서 호출해서 쓰는것임.
    fun sendMessage() {
        if (!bound) return
        val message = Message.obtain(null, MyService.MSG_SAY_HELLO, 0, 0)
        message.replyTo = Messenger(ResponseHandler())
        try {
            messenger?.send(message)
        } catch (e: RemoteException) {
            Log.e(TAG, "Failed to send message", e)
        }
    }

    inner class ResponseHandler : Handler() {
        override fun handleMessage(msg: Message) {
            when (msg.what) {
                MyService.MSG_REPLY -> {
                    Log.d(TAG, "Received reply from service")
                }
                else -> super.handleMessage(msg)
            }
        }
    }

    companion object {
        const val TAG = "MainActivity"
    }
}
  • 버튼 클릭할 때 sendMessage() 호출한다면 이런 식으로 쓸 수 있다.
 val sendMessageButton = findViewById<Button>(R.id.sendMessageButton)
        sendMessageButton.setOnClickListener {
            sendMessage()
        }

⛓ 사용법 - 외부앱 연동

  • 내일 마저 보자..

🔎 [2] AIDL

  • AIDL = Android Interface Definition Language
  • 두 프로세스 사이에 데이터를 주고받는 프로세스 간 통신을 구현할 때 쓰는 기법. 서비스 컴포넌트의 bindService()를 이용함.
  • AIDL을 사용하면 마샬링 과정을 대신 처리해줘서 편리하다고 한다..(프로세스->시스템->프로세스 시 시스템이 이해할 수 있는 원시 데이터로 바꿔주는 과정)
  • 메신저를 사용하면 더 쉽지만 메신저는 플랫폼 API를 사용할 수 밖에 없으므로 데이터 종류가 많을 때는 효율이 떨어질 수 있다. 그리고 메신저는 외부 요청을 싱글 스레드에서 처리하지만 AIDL은 여러 요청이 들어왔을 때 멀티스레드 환경에서 동시에 실행한다.

사용법

  1. .aidl 파일 작성

    • 외부 앱과 연동될 때 쓰이는 함수 인터페이스
  2. 서비스 컴포넌트

    • aidl 파일에 쓰인 함수 재정의해서 구현
    • 이 때 AIDL 파일을 구현한 객체가 아니라, 프로세스 간 통신을 대행해주는 Stub 객체 전달하여 재정의
    • 외부 앱과 연동이 목적이므로 매니페스트에서 암시적 인텐트로 실행되도록 추가
  • 자세한 코드는 실습에서,,,,

🔎 의뭉

  • 내가 이해한 바로는 서비스 컴포넌트는 백그라운드에서 실행되는 작업을 제어하는데 사용되는데, 메신저나 AIDL은 데이터를 주고 받는 것이니 차라리 콘텐츠 프로바이더와 연관이 있는것 아닌가? 라는 의문이 들었다.
  • 그러나 서비스 컴포넌트는 백그라운드 사용 외에도 컴포넌트 간 통신에 사용된다.

    🔎 차이점

    1. 서비스 컴포넌트 (Service Component):
      주로 백그라운드에서 실행되는 작업을 처리하기 위해 사용됩니다. 예를 들어, 오디오 재생, 파일 다운로드, 네트워크 호출 등의 작업을 수행할 때 서비스가 사용될 수 있습니다.
      주로 애플리케이션 구성 요소 간의 통신에 사용됩니다. 다른 컴포넌트 (예: 액티비티)가 서비스에 요청을 보내고, 서비스는 그 요청을 처리하고 결과를 반환합니다.
    2. 콘텐트 프로바이더 (Content Provider):
      데이터를 관리하고 다른 애플리케이션에 안전하게 액세스할 수 있도록 합니다. 이는 데이터를 공유하거나 다른 앱과의 상호 작용을 용이하게 합니다.
      일반적으로 데이터베이스에 연결되어 있으며, CRUD (Create, Read, Update, Delete) 작업을 수행하여 데이터를 관리합니다. 예를 들어, 연락처, 사진, 동영상 등의 데이터를 저장하고 다른 앱과 공유할 수 있습니다.
      콘텐트 프로바이더는 주로 데이터 액세스 및 공유를 위한 목적으로 사용됩니다.
  • 따라서 서비스는 주로 백그라운드 작업을 처리하고 다른 애플리케이션 구성 요소 간의 통신을 용이하게 하며, 콘텐트 프로바이더는 데이터 관리와 공유를 위해 사용됩니다. (출처 챗지피티;;)
  • 실습을 하면서 익숙해져야 완전히 이해가 될 것 같다.

참고한 블로그
https://velog.io/@jaeyunn_15/Android-Handler
https://velog.io/@chc0331/Messenger-Handler%EB%A5%BC-%EC%9D%B4%EC%9A%A9%ED%95%9C-%EC%84%9C%EB%B9%84%EC%8A%A4-IPC
https://bada744.tistory.com/134

profile
야금야금 앱 개발자

0개의 댓글