[Android Compose] Foreground Service 위치 정보 EventBus로 받아오기

: ) YOUNG·2024년 7월 21일
1

안드로이드

목록 보기
29/30
post-thumbnail
post-custom-banner

Foreground Service 위치 정보 EventBus

Compose Foreground Service EventBus



저번까지 Foreground Service로 위치 정보를 가져오는 것까지 하였고, 이번에는 위치 정보를 다른 곳으로 전송하는 것을 했습니다.

Foregrond Service에서 데이터를 받아오는 방법은 여러 가지가 있지만, 그 중 Broadcast Receiver는 옛날 프로젝트에서 사용해 본 경험이 있어 이번에는 EventBus를 사용해 보았습니다.

사실 EventBus는 원래 디자인 패턴 중의 하나인데, EventBus라는 라이브러리가 따로 있긴 합니다.
라이브러리는 EventBus 패턴을 편리하게 사용할 수 있게 한 것이므로 사실은 그냥 같은 개념의 EventBus이지만, 라이브러리일 뿐이라고 생각하시면 될 것 같습니다.

라이브러리를 사용하지 않을분들은 ViewModel과 Controller를 사용해서 EventBus 패턴을 직접 구현하셔도 됩니다.




EventBus 등록

먼저 EventBus의 라이브러리를 가져와야 합니다.
아래 링크로 가셔서 최신버전의 implementation을 사용하시면 됩니다.

저는 implementation("org.greenrobot:eventbus:3.3.1") 버전을 사용하였습니다.




EventBus 설정

먼저 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




Service에서 Event 보내기

다음은 서비스 구현체입니다.

서비스에서 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")
    }




@Composable에서 EventBus 수신하기

마지막으로 저는 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의 세부 사항 옵션이 많아 추가로 필요하신 정보를 한번 찾아보시는 걸 추천해 드립니다.
감사합니다.



이해가 안 되거나 궁금하신 게 있으신 분들은 댓글을 남겨주세요!
좋아요는 큰 힘이 됩니다!



post-custom-banner

0개의 댓글