백그라운드 작업은 필요하지만 액티비티와 데이터를 주고받을 일이 없는 등 서로 관련이 없다면 startService() 함수로 서비스를 실행하면 된다.
bindService()는 서비스와 액티비티가 상호작용 해야 할 때 호출하는 함수이다.
bind는 서비스가 실행되면서 자신을 실행한 곳에 객체를 바인딩, 전달한다는 의미이다.
// 서비스 코드
class MyBinder : Binder() {
fun funA(arg: Int) {}
...
}
override fun onBind(intent: Intent): IBinder? {
return MyBinder()
}
bindService() 함수로 서비스를 실행하면 onBind()가 실행되는데, 이 함수에는 반환 타입이 선언되어 있다.
IBinder 인터페이스를 구현한 객체를 전달한다.
// 액티비티 코드
val connection: ServiceConnection = object : ServiceConnection {
override fun onServiceConnected(name: ComponentName?, service: IBinder?) {
serviceBinder = service as MyService.MyBinder
}
override fun onServiceDisconnected(name: ComponentName>) {}
}
...
// 서비스에서 바인딩한 객체 함수 호출
serviceBinder.funA(10)
onServiceConnected 의 두 번째 매개변수가 서비스에서 전달한 객체이다.
bindServce() 함수로 서비스를 실행한 곳에는 IBinder를 구현한 객체를 바인딩한다.
API에서 제공하는 Messenger 객체를 바인딩하는 방법도 존재한다.
// 서비스 코드
class MyService : Service() {
lateinit var messenger: Messenger
internal class IncompingHandler(
context: context, private val applicationContext: Context = context.applicationContext)
: Handler(Loper.getMainLooper()) {
override fun HandleMessage(msg: Message) {
when (msg.what) {
10 -> Toast.makeText(applicationContext, "${msg.obj}", Toast.LENGTH_SHORT).show()
20 -> Toast.makeText(applicationContext, "${msg.obj}", Toast.LENGTH_SHORT).show()
else -> super.handleMessage(msg)
}
}
}
override fun onBind(intent: Intent): IBinder? {
messenger = Messenger(IncomingHandler(this))
return messenger.binder
}
}
handleMessage() 함수는 외부에서 서비스에 데이터를 전달할 때 자동으로 호출된다.
Messenger 객체의 binder 속성을 onBind() 함수의 결괏값으로 반환해 준다.
// 액티비티 코드
class MainActivity : AppCompatActivity() {
lateinit var messenger: Messenger
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
...
val intent = Intent(this, MyService::class.java)
bindService(intent, connection, Context.BIND_AUTO_CREATE)
}
val connection: ServiceConnection = object : ServiceConnection {
override fun onServiceConnected(name: ComponentName?, service: IBinder?) {
messenger = Messenger(service)
}
override fun onServiceDisconnected(name: ComponentName?) {}
}
}
프로세스 간 통신에서 주고받는 데이터는 Parcelabel이나 Bundle 타입이어야 한다.
따라서 데이터를 Bundle에 담고 다시 Message 객체에 담아서 전달한다.
val bundle = Bundle()
bundle.putString("data1", "hello")
bundle.putInt("data2", 10)
val msg = Message()
msg.what = 10
msg.obj = bundle
messenger.send(msg)
AIDL (Android Interface Definition Language)은 두 프로세스 사이에 데이터를 주고받는 프로세스 간 통신을 구현할 때 사용하는 기법이다.
기본적으로 안드로이드는 데이터를 시스템에 전달한 후 시스템이 다른 프로세스에 전달해준다.
이러한 절차에서 마샬링과정을 거쳐야 하는데, AIDL을 이용하면 이러한 작업을 대신 처리해주므로 편리하다.
MyAIDLInterface.aidl을 작성한다. 자바로 작성한 인터페이스이다.
package com.example.test_aidl;
interface MyAIDLInterface {
void funA(String data);
int funB();
}
AIDL 파일에 선언된 함수를 구현해 실제 작업을 처리하는 내용을 작성하는 곳은 서비스이다.
// 서비스 컴포넌트 구현
class MyAIDLService : Service() {
override fun onBind(intent: Intent): IBinder {
return object : MyAIDLInterface.Stub() {
override fun funA(data: String?) {}
override fun funB(): Int {return 10}
}
}
}
AIDL 파일을 구현한 객체가 아니라 프로세스 간 통신을 대행해 주는 Stub을 전달한다.
// 외부 앱의 서비스 실행
class MainActivity : AppCompatActivity() {
...
val connection: ServiceConnection = object : ServiceConnection {
override fun onServiceConnected(name: ComponentName?, service: IBinder?) {
aidlService = MyAIDLInterface.Stub.asInterface(service)
}
override fun onServiceDisconnected(name: ComponentName?) {}
}
...
aidlService.funA("hello")
}