안드로이드 앱 프로그래밍 튜토리얼에서 타이머를 구현하는 예시는 흔하게 찾아볼 수 있습니다. 보통 CountDownTimer
를 통해 타이머를 구현하곤 하는데, 이 타이머는 사용하기 매우 간단하고 편하지만 치명적인 문제가 있습니다.
직접 사용해보겠습니다.
간단하게 MainViewModel
에 남은 시간 데이터를 가지고 있는 countDownTimerDuration
이라는 MutableLiveData
를 선언하고, MainActivity
에서 CountDownTimer
를 하나 만들어서 버튼을 누르면 10초 동안 1초씩 줄어들며 텍스트뷰에 표시하게 했습니다. (countDownTimerDuration
은 텍스트뷰에 바인딩된 상태)
뭔가 이상합니다. CountDownTimer
의 countDownInterval
파라미터를 1000L
로 넘겨주었음에도 불구하고, 정확하게 값이 카운팅 되지 않는 것을 확인할 수 있습니다.😥
이 이슈는 이미 오래전부터 유명한 이슈였고, CountDownTimer
의 정확성이 보장되지 않기 때문에 많은 사람들이 커스텀 카운트 다운 타이머를 구현하여 사용하는 것을 볼 수 있었습니다.
물론 오차가 그렇게 크지 않기 때문에 뭐가 문제냐 라고 할 수도 있겠지만, 측정 시간이 더 길어진다면 오차는 점점 커질 것입니다.
Kotlin Coroutine으로 해결하기
위 문제를 코틀린 Coroutine을 사용하여 해결해봅시다. 방법은 간단합니다. MainViewModel
에 Job
객체 타입 변수를 하나 만들고 해당 Job
의 코루틴이 viewModelScope
를 Scope
로 가지도록 해줍니다. 코루틴 빌더인 launch
를 통해 별도의 옵션 없이 Job
객체를 만들면 만드는 동시에 실행되므로, 실행 시점을 제어하려면 launch
의 start
파라미터에 CoroutineStart.LAZY
를 넘겨줘야 합니다.
딜레이 걸린 시간을 계산해서 UI에 뿌려야하기 때문에 System.currentTimeMillis()
함수를 사용해서 시간 차를 계산해줍니다.
이제 실행해보도록 하겠습니다.
정확하게 카운팅 될 뿐만 아니라 ViewModelScope
를 scope로 가지는 Job
이기 때문에 화면 회전과 같은 Configuration Change 이벤트에도 멀쩡하게 잘 동작하는 것을 확인할 수 있습니다🎉
이상으로 Coroutine을 사용하여 간단하게 타이머를 구현해보았습니다.