Service
- StartService()는 서비스를 호출만 합니다.
- BindService()는 서비스를 호출해서 값을 주고 받을 수 있습니다.
BindService
- bindService는 startService와는 달리 하나 이상의 클라이언트 컴포넌트 간의 통신을 구현할 수 있습니다.
- 서비스를 실행시켜두고 필요할때마다 서비스의 메서드에 접근하여 통신할 수 있습니다.
- Service(background)가 실행되고 있을때 화면(activity)에서 Service의 메서드를 호출하고 결과를 받아서 보여주는 등 상호작용할 경우 사용합니다.
BindService의 생명주기
⁂ 서비스를 계속 실행하면서 바인딩도 제공해야하는 경우
-> startService를 통해 서비스를 실행시키고 클라이언트가 bindService()를 사용해 binding 할 수있다.
=> 이 경우에는 반드시 stopSelf() 또는 stopService()를 호출해야한다.
why? 바인딩을 모두 해제해도 서비스가 소멸되지 않음
=> onStartCommand()와 onBind() 둘 다를 구현해야함
BindService 구현하기
- Local로 구현
-> 서비스와 클라이언트가 동일 프로세스에서 실행되면서 private(다른 애플리케이션에서 사용 불가)인 경우라면 Binder 클래스의 서브 클래스를 생성하여 구현 가능
- Local이 아닌 경우 (다른 프로세스에서 실행)
-> Messenger와 Handler를 사용
-> AIDL(Android Interface Definition Language) 사용
⁂
- Service를 local로 구현하는 것은 thread를 사용하는 것과 동작이 비슷합니다.
- 통신을 하는 모든 Thread는 queue를 가지고 있습니다.
-> ThreadHandler가 queue의 값을 처리하고
-> Looper가 Handler에게 queue의 값을 전달합니다.
-> 모든 thread는 handler를 가질 수도 가지지 않을 수도 있습니다.
AIDL(Android Interface Definition Language)
- AIDL은 android 에서 사용하는 IDL입니다.
- IDL : 같은 언어를 사용하지 않는 소프트웨어 컴포넌트 사이의 통신을 가능하게 합니다.
-> ex) c++과 java간의 통신을 구현할 때
=> 통신의 최하단 부분은 IDL이 구현해주고 개발자는 interface만 구현하도록 하는 것입니다.
Binder
- Binder는 기본적으로 Process간 통신을 제공합니다.
- IPC/ Binder
-> IPC(Inter Process Communication) : 프로세스들 사이에 서로 데이터를 주고받는 행위 또는 그에 대한 방법이나 경로
=> 프로세스 통신, 리눅스 커널에서 Binder를 이용하여 프로세스간 메시지를 주고 받도록 구현되어 있습니다.
=> process는 kernel의 내부의 일정 공간을 공유하여 함수를 호출, 이를 Binder Driver가 수행합니다.
⁂ 이때 Binder는 android framework중 linux kernel에 있는 Binder입니다.
- Parcel / Parcelable
-> Binder는 데이터를 Parcel 형태로 전달하므로 안드로이드에서는 Parcelable 클래스를 활용합니다.
=> 안드로이드에서 직접 메모리 공간에 object를 읽고 쓰는 클래스를 Parcelable 클래스라고 합니다.
⁂ Serializable을 사용해도 무관합니다.
Local Service 생성하기
Binder를 통해 Activity와 Service가 통신하는 프로세스
- Activity가 ServiceConnection을 생성합니다.
- Intent를 통해 bindService() 매개변수로 Service Connection 인스턴스를 넣고 bindService를 시작합니다. -> Service가 만들어집니다.
- onBind를 통해 Binder를 생성합니다.
- Service Connection의 내부 메서드인 onServiceConnected의 인자로 Binder가 주어집니다.
이제 하나하나 코드를 살펴 보겠습니다.
- Activity가 ServiceConnection을 생성합니다.
//MainActivity
connection = object : ServiceConnection{
override fun onServiceConnected(name: ComponentName?, service: IBinder?) {
val binder = service as BoundService.MyLocalBinder
myService = binder.getService()
}
override fun onServiceDisconnected(name: ComponentName?) {
}
}
- Intent를 통해 bindService() 매개변수로 Service Connection 인스턴스를 넣고 bindService를 시작합니다. -> Service가 만들어집니다.
//MainActivity
var intent = Intent(this, BoundService::class.java)
bindService(intent,connection, BIND_AUTO_CREATE)
-> bindService의 매개변수는 각각 intent, 성공하면 callback받을 곳, FLAG(binding option)의 인자를 받습니다.
- onBind를 통해 Binder를 생성합니다.
//Service
private val mBinder : IBinder = MyLocalBinder()
override fun onBind(intent: Intent?): IBinder {
return mBinder
}
inner class MyLocalBinder : Binder(){
fun getService(): BoundService{
return this@BoundService
}
}
- Service Connection의 내부 메서드인 onServiceConnected의 인자로 Binder가 주어집니다.
-> 앞에서 만든 connection의 service: IBinder? 에 Service에서 만든 mBinder가 들어갑니다.
Remote Service 생성하기
local이 아닐 경우
-
다른 프로세스에서도 Service를 사용해야 한다면 Messenger Handler를 이용해서 구현하는 방법이 있습니다.
-> Message에 data를 담아서 다른 프로세스에 전해주는 것입니다.
-> Android 11부터는 package visibility가 강화되어 외부의 Service를 호출한다면 AndroidMenifest.xml 파일에 queries를 선언해야 합니다.
=> 선언함으로 인해 어떤 Service를 호출하는지 명확하게 알 수 있습니다.
-
Handler로 처리하면 순차적으로 Task를 처리하여 동시에 Task를 처리할 수 없습니다.
AIDL을 통한 서비스 구현
- AIDL를 통해서도 별도의 프로세스에서 서비스를 사용할 수 있습니다.
- AIDL을 사용하면 여러 요청을 동시에 요청하고 처리할 수 있습니다.
AIDL 생성하기
- aidl 파일 생성 (textfile)
- 인터페이스 구현
- Android SDK는 Java 언어로 인터페이스를 생성함
- 해당 Interface는 Binder를 상속하고 AIDL 인터페이스 메서드를 구현해주는 Stub라는 내부 추상 클래스가 있음
- Activity에서 서비스 바인딩
- Service를 구현한고 onBind()를 오버라이딩하여 Stub 추상클래스를 상속받은 클래스의 객체를 생성하여 반환함
- Activity에서 구현한 서비스를 바인딩함