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이상인 앱에서는 디바이스에 설치된 다른 앱 목록을 알 수 없고 매니페스트 파일에 지정한 앱의 정보만 가져올 수 있다.)