(RunWithMe 리팩토링) 1. LifecycleService와 Service의 차이점
안녕하세요
RunWithMe 리팩토링 프로젝트에서 팀장과 안드로이드 개발을 맡은 서경원입니다.
RunWithMe 프로젝트를 리팩토링하면서 배우게 된 내용에 대해서 설명해보려합니다.
이해가 안되는 내용이나 제가 잘못 적은 부분이 있다면 꼭 댓글 남겨주세요.
LifecycleService 이건 뭐지??
이전 RunWithMe 프로젝트 코드를 들여다보던 중에 러닝에 관한 서비스를 LifecycleService를 상속받아 사용하는 것을 보게되었습니다.
LifecycleService는 무엇이고 그냥 Service와의 차이점에 대해서 궁금증이 생기게 되었습니다.
바로 LifecycleService를 들여다보았습니다.
Service와 달리 LifecycleOwner를 implements 하고 있는 것을 볼 수 있습니다.
그렇다면 LifecycleOwner는 뭐지?
LifecycleOwner는 Lifecycle을 구현하고 있으며, 앱 구성 요소의 생명주기 상태 변경에 따라 이벤트를 수신할 수 있습니다. LifecycleOwner를 통해 해당 구성 요소의 라이프사이클 상태를 관리할 수 있습니다.
getLifecycle()
메서드는 Lifecycle 객체를 반환하고 이를 통해 앱 구성 요소의 생명주기 상태를 추적하고, 이벤트를 수신할 수 있습니다.
‘LifecycleOwner를 통해 라이프사이클 상태를 관리할 수 있는데 어떻게 하는거지?’ 라는 의문이 생길 수 있습니다. 아래 예를 보면 잘 이해되실 겁니다.
예) lifecycleScope
lifecycleScope의 내부입니다. LifecycleOwner에 속한 것을 볼 수 있습니다.
기존에 CoroutineScope의 경우에는 부모 뷰가 시작할 때 launch를 시작하여 destroy될 때 해제시켜주어야 Coroutine 수행을 더이상 진행하지 않도록 하고 메모리 누수를 막을 수 있었습니다.
하지만, lifecycleScope를 사용한다면? LifecycleOwner가 부모 뷰의 라이프사이클을 따라가기 때문에 부모 뷰가 destory되는 경우 자동으로 해제되게 됩니다.
즉, LifecycleService에서 사용하게 된다면 Service가 destory될 때 lifecycleScope의 job도 해제되기 때문에 따로 해제해줄 필요가 없어 손쉽게 관리할 수 있습니다.
LifecycleService는 LifecycleOwner를 구현한 Service의 확장 클래스로 생명 주기를 관리하기 쉽다는 장점이 있습니다. 하지만 기존 코드에서는 lifecycleScope를 사용하지 않고 CoroutineScope를 그대로 사용하고 있었습니다. 이를 lifecycleScope로 변경해주었습니다.
// 기존 코드
private fun startTimer(){
addEmptyPolyline()
isTracking.postValue(true)
timeStarted = System.currentTimeMillis()
isTimerEnabled = true
// 메모리 누수의 원인이 될 수 있음
// 해제하는 코드도 없었기 때문
CoroutineScope(Dispatchers.Main).launch {
// 위치 추적 상태일 때
while (isTracking.value!!){
// 현재 시간 - 시작 시간 => 경과한 시간
lapTime = System.currentTimeMillis() - timeStarted
// 총시간 (일시정지시 저장된 시간) + 경과시간 전달
timeRunInMillis.postValue(totalTime + lapTime)
// 알림창에 표시될 시간 초 단위로 계산함
if(timeRunInMillis.value!! >= lastSecondTimestamp + 1000L){
timeRunInSeconds.postValue(timeRunInSeconds.value!! + 1)
lastSecondTimestamp += 1000L
}
delay(TIMER_UPDATE_INTERVAL)
}
// 위치 추적이 종료(정지) 되었을 때 총시간 저장
totalTime += lapTime
}
}
// 변경 코드
private fun startTimerJob() {
lifecycleScope.launch(Dispatchers.Main) {
// 러닝 중 일 때
while (isRunning.value!!){
// 현재 시간 - 시작 시간 => 경과한 시간
lapTime = System.currentTimeMillis() - timeStarted
// 총시간 (일시정지 시 저장된 시간) + 경과시간 전달
timeRunInMillis.postValue(totalTime + lapTime)
// 알림창에 표시될 시간 초 단위로 계산함
if(timeRunInMillis.value!! >= lastSecondTimestamp + 1000L){
timeRunInSeconds.postValue(timeRunInSeconds.value!! + 1)
lastSecondTimestamp += 1000L
}
delay(50L)
}
// 위치 추적이 종료(정지) 되었을 때 총시간 저장
totalTime += lapTime
}
}
(추가적으로 다른 로직을 빼주어 Timer 작업만 하는 메소드로 만들어주었습니다.)
결과적으로 LifecycleOwner에 대해 공부하면서 생명 주기에 따른 관리와 메모리 누수 방지를 위한 생각을 해볼 수 있었습니다!
'Android 일지 > 리팩토링' 카테고리의 다른 글
3. 경로 최적화로 좌표 데이터 약 73% 감소 (2) | 2023.10.17 |
---|---|
6. SharedPreference에서 DataStore로 변경하여 데이터 일관성 문제 해결하기 (0) | 2023.05.30 |
5. CustomView로 재사용성 향상 (0) | 2023.05.27 |
4. EventFlow 도입 (0) | 2023.05.26 |
2. bindService를 적용하여 메모리 누수 방지하기 (1) | 2023.05.25 |