저번까지 Foreground Service로 위치 정보를 가져오는 것까지 하였고, 이번에는 위치 정보를 다른 곳으로 전송하는 것을 했습니다.
Foregrond Service에서 데이터를 받아오는 방법은 여러 가지가 있지만, 그 중 Broadcast Receiver는 옛날 프로젝트에서 사용해 본 경험이 있어 이번에는 EventBus를 사용해 보았습니다.
사실 EventBus는 원래 디자인 패턴 중의 하나인데, EventBus라는 라이브러리가 따로 있긴 합니다.
라이브러리는 EventBus 패턴을 편리하게 사용할 수 있게 한 것이므로 사실은 그냥 같은 개념의 EventBus이지만, 라이브러리일 뿐이라고 생각하시면 될 것 같습니다.
라이브러리를 사용하지 않을분들은 ViewModel과 Controller를 사용해서 EventBus 패턴을 직접 구현하셔도 됩니다.
먼저 EventBus의 라이브러리를 가져와야 합니다.
아래 링크로 가셔서 최신버전의 implementation
을 사용하시면 됩니다.
저는 implementation("org.greenrobot:eventbus:3.3.1")
버전을 사용하였습니다.
먼저 EventBus를 사용하기 위해서 몇 가지 구현이 필요합니다. @Subscribe를 통해서 Event를 구독할 수 있는데, Event가 여러 가지 일 수 있기 때문에 특정 이벤트에 대한 정의가 미리 필요합니다.
따라서 EventType을 정의하는 class를 구현하였습니다.
현재는 EventBus
에서 위치 정보만 수집하여 전달하지만, 추후에 다른 정보도 전달할 수 있기 때문에 확장성을 위하여 저는 sealed class
를 사용하였습니다.
import com.google.android.gms.maps.model.LatLng
sealed class EventBusEvent {
data class CurrentLocationEvent(val location: LatLng) : EventBusEvent()
} // End of AppEvent class
다음은 서비스 구현체입니다.
서비스에서 EventBus를 등록한 후 해당 EventBus에 위치 정보를 담아 보내면 됩니다.
이전 개발 코드도 함께 있으니 EventBus 부분만 눈여겨보시면 될 것 같습니다.
EventBus에서 사용되는 부분은 주석으로 //EventBus
를 붙여놓겠습니다.
Service.kt
class RecordingService : Service() {
...
private lateinit var fusedLocationProviderClient: FusedLocationProviderClient
private lateinit var locationCallback: LocationCallback
override fun onCreate() {
super.onCreate()
context = applicationContext
fusedLocationProviderClient = LocationServices.getFusedLocationProviderClient(context)
} // End of onCreate()
override fun onBind(p0: Intent?): IBinder? {
return null
} // End of onBind()
override fun onStartCommand(intent: Intent?, flags: Int, startId: Int): Int {
when (intent?.action) {
Actions.START.toString() -> start()
Actions.STOP.toString() -> stopSelf()
}
return super.onStartCommand(intent, flags, startId)
} // End of onStartCommand()
@SuppressLint("MissingPermission")
private fun start() {
val intent = Intent(this, MainActivity::class.java).apply {
addFlags(Intent.FLAG_ACTIVITY_NEW_TASK or Intent.FLAG_ACTIVITY_SINGLE_TOP)
}
val pendingIntent = PendingIntent.getActivity(
this, 0, intent, PendingIntent.FLAG_MUTABLE or PendingIntent.FLAG_UPDATE_CURRENT
)
val locationInterval = 5000L
val locationRequest =
LocationRequest.Builder(Priority.PRIORITY_HIGH_ACCURACY, locationInterval)
.setWaitForAccurateLocation(false).build()
locationCallback = object : LocationCallback() {
override fun onLocationResult(locationResult: LocationResult) {
super.onLocationResult(locationResult)
val location = locationResult.locations[0]
Log.d(TAG, "onLocationResult: $location")
}
}
fusedLocationProviderClient.requestLocationUpdates(
locationRequest, locationCallback, Looper.getMainLooper()
)
val notification = NotificationCompat.Builder(this, "test_channel")
.setContentTitle("test!")
.setContentIntent(pendingIntent).setContentText("Elapsed time").setOngoing(true).build()
startForeground(1, notification)
} // End of start()
override fun onDestroy() {
fusedLocationProviderClient.removeLocationUpdates(locationCallback)
super.onDestroy()
}
enum class Actions {
START, STOP
} // End of Actions class
onDestroy()
에서 fusedLocationProviderClient
를 통해 위치 정보를 가져오는 객체를 지웁니다.
이벤트 전송은 EventBus.getDefault().post()
를 통해서 보내게 되는데, sealed class로 만들어 놓은 EventBusEvent.CurrentLocationEvent
에 담아 위치 정보를 전송합니다.
@Subscribe(threadMode = ThreadMode.MAIN)
public fun onCurrentLocationEvent(event: EventBusEvent.CurrentLocationEvent) {
Log.d(TAG, "onMessage: $event")
}
마지막으로 저는 Screen에서 사용할 생각이었으므로, @Composable fun에서 Event를 받아오겠습니다.
EventBus를 수신해서 사용하기 위해서는 먼저 EventBus를 등록하고, @Subscribe
함수를 구현해야 합니다.
EventBus를 등록하는 함수는 EventBus.getDefault().register()
해제하는 함수는 EventBus.getDefault().unregister()
입니다.
여기서 구독하는 EventBus가 당연히 있어야하므로 구독 함수 reg
를 넣어주시면 됩니다.
Screen.kt
@Composable
fun TestScreen() {
...
/* EventBus */
DisposableEffect(key1 = Unit) {
val reg = object {
@Subscribe
fun onCurrentLocationEvent(event: EventBusEvent.CurrentLocationEvent) {
Log.d(TAG, "onCurrentLocationEvent: $event")
}
}
EventBus.getDefault().register(reg)
onDispose {
EventBus.getDefault().unregister(reg)
}
}
...
이렇게 하면 Foreground Service 에서 수집되는 위치정보를 EventBus를 사용하여 정보를 전달 받을 수 있습니다.
이제 Screen에서 데이터를 사용하면 될 것 같습니다.
이외에도 EventBus의 세부 사항 옵션이 많아 추가로 필요하신 정보를 한번 찾아보시는 걸 추천해 드립니다.
감사합니다.
이해가 안 되거나 궁금하신 게 있으신 분들은 댓글을 남겨주세요!
좋아요는 큰 힘이 됩니다!