훌륭한 개발자가 되기 위하여
[Android | Kotlin] 바인딩 서비스 본문
IBinder 객체 바인딩
백그라운드 작업은 필요하지만 액티비티와 데이터를 주고받을 일이 없는 등 서로 관련이 없다면 startService( ) 함수 사용
서비스와 액티비티가 상호작용 해야 할 때 bindService( ) 함수 사용
'bind'는 서비스가 실행되면서 자신을 실행한 곳에 객체를 바인딩한다. (객체를 전달)
서비스 코드
- onBind( ) 함수의 반환 타입은 IBinder 인터페이스
- 서비스를 실행한 곳에서 이 클래스의 함수를 호출하면서 매개변수와 반환값으로 데이터를 주고받습니다.
// 서비스에서 객체 바인딩
class MyBinder : Binder() {
fun funA(arg: Int) { }
fun funB(arg: Int): Int = arg * arg
}
override fun onBind(intent: Intent): IBinder? {
return MyBinder()
}
액티비티 코드
- onBind( ) 함수에서 반환한 객체를 ServiceConnection 인터페이스를 구현한 객체의 onServiceConnected( ) 함수로 받을 수 있습니다.
// 액티비티에서 객체를 전달받아 사용
val connection: ServiceConnection = object : ServiceConnection {
override fun onServiceConnected(name: ComponentName?, service: IBinder?) {
serviceBinder = service as MyService.MyBinder
}
override fun onServiceDisconnected(name: ComponentName?) { }
}
♬서비스와 액티비티 간의 데이터를 주고받을 때 인텐트의 엑스트라 데이터는 인텐트로 특정 컴포넌트를 실행할 때 전달해야 하는 데이터를 의미하는 반면, 바인드 서비스는 인텐트로 서비스가 실행된 상태에서 발생하는 데이터를 주고받는 방법입니다. (액티비티와 서비스가 이미 실행되고 있는 상황에서 서로 데이터를 주고받는 것입니다.)
메신저 바인딩
- API에서 제공하는 Messenger 객체를 바인딩하는 방법
- Messenger 객체를 이용하는 방법은 프로세스 간 통신할 때도 사용
- handleMessage( ) 함수는 외부에서 서비스에 데이터를 전달할 때 자동으로 호출
- 전달한 데이터는 Message 타입
- Message의 what 값으로는 어떤 성격의 데이터인지를 구분하며 obj 속성으로는 전달된 데이터를 가져옵니다.
- onBind( ) 함수의 반환값으로 Messenger 객체를 생성하면서 생성자 매개변수로 Handler를 구현한 객체를 지정
// 메신저 객체를 이용하는 서비스 코드
class MyService : Service() {
lateinit var messenger: Messenger
internal class IncomingHandler(
context: Context,
private val applicationContext: Context = context.applicationContext
) : Handler(Looper.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
}
}
- onServiceConnected( ) 함수의 매개변수로 넘어온 객체를 Messenger의 생성자 매개변수에 지정
// 메신저 객체를 이용하는 액티비티 코드
class MainActivity : AppCompactActivity() {
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?) { }
}
}
// 서비스에 데이터 전달
val msg = Message()
msg.what = 10
msg.obj = "hello"
messenger.send(msg)
외부 앱 연동
- 외부 앱의 서비스를 bindService( ) 함수로 실행하려면 먼저 서비스를 등록한 매니페스트에 외부 앱을 연동할 수 있게끔 <intent-filter>를 선언
- bindService( ) 함수로 실행하는 엡에서 외부 앱에 접근할 수 있도록 매니페스트에 queries 등록
// 매니페스트에 인텐트 필터 선언
<service android:name=".MyService"
android:enabled="true">
<intent-filter>
<action android:name="ACTION_OUTER_SERVICE"/>
</intent-filter>
</service>
// 매니페스트에 패키지 정보 등록
<queries>
<package android:name="com.example.test_outter"/>
</queries>
- 외부 앱을 연동하고자 한다면 bindService( ) 함수로 발생하는 인텐트에 실행 대상인 앱의 패키지명을 명시
- 프로세스 간 통신에서는 주고받는 데이터는 Parcelable이나 Bundle 타입이어야 합니다.
// 실행할 앱의 패키지명 명시
val intent = Intent("ACTION_OUTER_SERVICE")
intent.setPackage("com.example.test_outter")
bindService(intent, connection, Context.BIND_AUTO_CREATE)
// 번들 객체 이용
val bundle = Bundle()
bundle.putString("data1","hello")
bundle.putInt("data2",10)
val msg = Message()
msg.what = 10
msg.obj = bundle
messenger.send(msg)
AIDL 통신 기법
- AIDL은 두 프로세스 사이에 데이터를 주고받는 프로세스 간 통신
- 서비스 컴포넌트의 bindService( ) 함수를 이용
- 프로세스 간 통신은 메신저를 이용하면 AIDL보다 더 쉽게 구현
- 메신저를 이용하는 방법은 플랫폼에서 제공하느 API를 이용해야 하므로 주고받는 데이터의 종류가 많을 때는 효율이 떨어질 수 있습니다.
- 메신저는 모든 외부 요청을 싱글 스레드에서 처리
- AIDL은 여러 요청이 들어오면 멀티 스레드 환경에서 동시에 실행
서비스를 제공하는 앱
- AIDL을 이용하려면 확장자기 aidl인 파일을 만들어야 합니다.
- AIDL 파일에 선언된 함수를 구현해 실제 작업을 처리하는 내용을 작성하는 곳은 서비스
- AIDL은 바인딩 서비스를 이용하므로 onBind( ) 함수에서 서비스를 인텐트로 실행한 곳에 객체를 전달
- 이때 AIDL 파일을 구현한 객체가 아니라 프로세스 간 통신을 대행해 주는 Stub를 전달
// 서비스 컴포넌트 구현
class MyAIDLService : Service() {
override fun onBind(intent: Intent): IBinder {
return object : MyAIDLService.Stub() {
override fun funA(data: String?) { }
override fun funB(): Int = 10
}
}
}
서비스를 이용하는 외부 앱
- bindService( ) 함수를 이용하며 인텐트에 패키지 정보를 포함
- AIDL 서비스를 이용하는 앱도 AIDL 서비스를 제공하는 앱에서 만든 AIDL 파일을 가지고 있어야 합니다.
// 매니페스트에 패키지 정보 등록
<queries>
<package android:name="com.example.test_outter"/>
</queries>
// 외부 앱의 서비스 실행
class MainActivity : AppCompatActivity() {
lateinit var aidlService: MyAIDLInterface
// (... 생략 ...)
override fun onCreate(savedInstanceState: Bundle?, persistentState: PersistableBundle?) {
super.onCreate(savedInstanceState, persistentState)
// (... 생략 ...)
val intent = Intent("ACTION_AIDL_SERVICE")
intent.setPackage("com.example.test_outter")
bindService(intent, connection, Context.BIND_AUTO_CREATE)
}
// (... 생략 ...)
val connection: ServiceConnection = object : ServiceConnection {
override fun onServiceConnected(name: ComponentName?, service: IBinder?) {
aidlService = MyAIDLInterface.Stub.asInterface(service)
}
override fun onServiceDisconnected(name: ComponentName?) {
Log.d("kim", "onServiceDisconnected...")
}
}
}
'안드로이드' 카테고리의 다른 글
[Android | Kotlin] 잡 스케줄러 (1) | 2023.12.31 |
---|---|
[Android | Kotlin] 백그라운드 제약 (0) | 2023.12.31 |
[Android | Kotlin] 서비스 이해하기 (0) | 2023.12.29 |
[Android | Kotlin] 시스템 상태 파악하기 (0) | 2023.12.28 |
[Android | Kotlin] 브로드캐스트 리시버 이해하기 (0) | 2023.12.28 |