Messenger, Handler를 이용한 서비스 IPC

최희창·2023년 1월 26일
0

Service

목록 보기
1/1

1. 개요

프로젝트 중 다른 Process 간의 Service 통신 구현이 필요함에 따라 Messenger, Handler를 이용해 구현해본다.
(AIDL을 이용하는 방법이 있으나 멀티 프로세싱은 필요하지 않으므로 보류한다.)

2. 시나리오

Client App -> Server App 로 Service를 Binding 시키고 Client에서 Server Service의 원하는 기능을 실행시킨다.

2-1. Service App

class TouchService : Service() {
	// Client와의 통신을 위해 Handler를 생성자 매개변수로 갖는 Messenger 객체가 필요하다.
    private val messenger: Messenger by lazy { Messenger(TouchServiceHandler(this)) }

    override fun onCreate() {
        super.onCreate()
        Log.d(TAG, "TouchService onCreate")
    }

    override fun onBind(intent: Intent): IBinder {
        Log.d(TAG, "TouchService onBind")
        //Messenger의 binder를 반환한다.
        return messenger.binder
    }
}

-> Service 구현

class TouchServiceHandler(private val context: Context) :
    Handler(Looper.getMainLooper()) {

    override fun handleMessage(msg: Message) {
        val bundle = msg.data
        when (msg.what) {
            DRAW_VIEW -> {
                draw()
            }
            REMOVE_VIEW -> {
            }
            else -> super.handleMessage(msg)
        }
    }

-> 일반 Handler와 다를 점이 없다.

※ Messenger 객체란?

기본적으로 Handler는 하나의 process 내에서 여러 Thread들이 통신할 때 사용하는 녀석이다. 그리고 이 Handler를 process들끼리의 통신에 사용할 수 있게 하는 녀석이 Messenger이다.
Messenger를 이용해서 Handler를 감싸면 Marshaling 부분을 편하게 알아서 해주어 process 사이에서 Handler를 사용할 수 있게 해준다.

2-2. Client App

class MainActivity : AppCompatActivity() {

    private var touchService: Messenger? = null
    private var bound: Boolean = false

	//bound service 이용을 위한 Connection 객체
    private val connection = object : ServiceConnection {
        override fun onServiceConnected(name: ComponentName?, service: IBinder?) {
            Log.d(TAG, "TouchService Connected")
            touchService = Messenger(service)
            bound = true
        }

        override fun onServiceDisconnected(name: ComponentName?) {
            touchService = null
            bound = false
        }
    }
    
    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContentView(binding.root)

		//Service 이용시 명시적 인텐트를 이용해 선언해주어야 한다.
        binding.connectTouchService.setOnClickListener {
            val res = applicationContext.bindService(Intent().apply {
                setClassName(SERVICE_PKG, SERVICE_CLS)
            }, connection, Context.BIND_AUTO_CREATE)
            Log.d(TAG, "connect service : $res")
        }

        binding.disconnectTouchService.setOnClickListener {
            Log.d(TAG, "disconnect service")
            applicationContext.unbindService(connection)
        }
    }
    

-> client app activity 구현

<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
    package="com.example.clientapp">

    <queries>
        <package android:name="com.example.viewidcollecttest" />
    </queries>
    ...
</manifest>

-> 다른 process의 패키지 정보를 알기 위해 명시해주어야 한다.
(targetSdkVersion이 11이상인 앱에서는 디바이스에 설치된 다른 앱 목록을 알 수 없고 매니페스트 파일에 지정한 앱의 정보만 가져올 수 있다.)

profile
heec.choi

0개의 댓글