서비스와 뷰(액티비티,프래그먼트)와 통신하기 위해서 여러가지 방법이 있지만 AIDL을 사용해보았다.
클라이언트와 서비스가 모두 동의한 프로그래밍 인터페이스를 정의하여 프로세스 간 통신(IPC)으로 서로 통신할 수 있다.
먼저 AIDL파일을 만들어준다.
File -> New -> AIDL File
우선 콜백과 서비스를 만들어주었다.
// IStepService.aidl
package com.example.plogging;
import com.example.plogging.IStepServiceCallback;
interface IStepService {
boolean addCallback(IStepServiceCallback callback);
boolean removeCallback(IStepServiceCallback callback);
void startTimer();
void stopTimer();
}
// IStepServiceCallback.aidl
package com.example.plogging;
// Declare any non-default types here with import statements
interface IStepServiceCallback {
void onStepChanged(int stepCount);
void onMiniutesChanged(int minutes);
}
AIDL파일은 자바 기반으로 되어있어서 자바 기반으로 작성해준다.
파일을 만들고 빌드를 해줘야 import할 수 있기 때문에 빌드를 해줘야 한다.
Build -> Make Build
class SomeService : Service(){
val listeners = arrayListOf<IStepServiceCallback>()
private val binder = object : IStepService.Stub(){
override fun addCallback(callback: IStepServiceCallback?): Boolean {
callback?.let {
listeners.add(it)
}
return true
}
override fun removeCallback(callback: IStepServiceCallback?): Boolean {
callback?.let {
listeners.remove(it)
}
return true
}
override fun startTimer() {
this@SomeService.startTimer()
}
override fun stopTimer() {
this@SomeService.stopTimer()
}
// 데이터가 변경되는 시점에 리스너들에게 데이터가 변경되었다는 것을 알려준다.
private fun dataChanged(step:Int){
listeners.forEach {
it.onStepChanged(currentStep)
}
}
}
여러개의 뷰들이 서비스에서 데이터를 받을 수 있으므로 listeners callback리스트들로 선언해주었다.
그리고 서비스의 override method인 onBind에 biner로 리턴해준다.
override fun onBind(p0: Intent?): IBinder {
return binder
}
class HomeFragment:Fragment(){
private var bounded = false
private var iStepService: IStepService? = null
private lateinit var stepCallback: IStepServiceCallback
private var bounded = false
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState)
stepCallback = object : IStepServiceCallback.Stub() {
override fun onStepChanged(stepCount: Int) {
viewModel.currentStep.postValue(stepCount)
bounded = true
}
override fun onMiniutesChanged(minutes: Int) {
viewModel.currentMinutes.postValue(minutes)
bounded = false
}
}
}
private val connection = object : ServiceConnection {
override fun onServiceConnected(p0: ComponentName?, service: IBinder?) {
Log.d("TAG", "onServiceConnected: attatched")
iStepService = IStepService.Stub.asInterface(service)
iStepService!!.addCallback(stepCallback)
iStepService!!.startTimer()
}
override fun onServiceDisconnected(p0: ComponentName?) {
Log.d("TAG", "onServiceDisconnected: disconneced")
iStepService = null
}
}
}
callback을 초기화해주고 바인드서비스를 만들기 위한 ServiceConnection을 선언해주었다.
그리고 onStart,onStop에 register/unregister해준다
override fun onStart() {
super.onStart()
if(bounded){
val intent = Intent(requireContext(),StepCounterService::class.java)
requireActivity().bindService(intent,connection, Context.BIND_AUTO_CREATE)
}
}
override fun onStop() {
super.onStop()
if(bounded){
iStepService?.removeCallback(stepCallback)
requireActivity().unbindService(connection)
}
}