Lifecycle.State와 repeatOnLifecycle에 대해서
오늘은 Lifecycle State에 대해서 알아보겠습니다.
안드로이드의 Lifceycle에 대해서는 많이 들어보셨겠지만, Lifecycle State는 생소할 수도 있습니다.
왜나하면, 저희는 모르게도 많이 사용되기도 하고, 쓰지만서도 제대로 알지 못하고 사용하기 때문입니다.
먼저 Lifecycle State는 어디서 쓰일까요?
기본적으로 livedata나 flow를 사용할 때 아래와 같은 코드를 아무 생각 없이 사용하시진 않나요 ?
a.observe(viewLifecycleOwner) {
// do something
}
viewLifecycleOwner.lifecycleScope.launch {
repeatOnLifecycle(Lifecycle.State.STARTED) {
movieList.collect {
// do something
}
}
}
알고 쓰셨다구요?? 그럼 여기서 나가시면 됩니다 ! 모르시는 분들만 읽으시면 좋습니다.
먼저 기초 지식으로 알아두면 좋은 지식을 말해보자면, LiveData는 안드로이드의 라이프 사이클을 알고 자동으로 라이프 사이클에 따라 observer를 구독해주고 취소해줍니다.
하지만 Flow는 안드로이드 자체의 기능이 아닌, Kotlin의 기능이기 때문에 안드로이드의 라이프 사이클을 알지 못합니다. 그렇기 때문에 라이프 사이클에 맞게 구독, 구독 취소를 해주기 위해 위와 같이 코드들이 추가되게 됩니다.
그런데, 저기 써있는 STARTED는 무엇을 뜻할까요? 단순 onStart()를 뜻할까요 ? 아닙니다.
여기서 말하는 Lifecycle.State.STARTED는 STATE가 started 라는 뜻입니다. 이는 공식문서의 사진을 보도록 하겠습니다.
위의 사진을 보면, STARTED는, 안드로이드 라이프사이클에서 onStart 이후와 onPause 이전을 뜻합니다. 내부의 설명을 보죠!
Lifecycle.EVENT의 STARTED는 start 이후, onPause 이전에 불린다고요 !
그렇다면 이제, repeatOnLifecycle의 내부를 보면 아래와 같은 코드를 볼 수 있습니다. STARTED를 작성해 주었을 때,
startWorkEvent는 Lifecycle.Evenet.upTo(STARTED), Lifecycle.Event.downFrom(STARTED)라는 코드가 실행이 됩니다.
upTo의 코드를 보면 아래와 같습니다.
STARTED를 전달해 주었을 때, ON_START를 반환해줍니다. 여기서 우리가 알고있는 ON_START 와 같은 것들을 Lifcycle Event라고 합니다. (또한 LifecycleEvent 호출 이후에 우리가 알고 있는 안드로이드 라이프 사이클을 호출합니다)
이번에는 downFrom을 보면 아래와 같습니다.
그런데, 여기서 헷갈리면 안되는 점이 있습니다. downTo와 downFrom의 차이가 있습니다.
downTo는 우리의 예상처럼 STARTED에서 ON_PAUSE를 반환해주지만, downFrom은 STARTED에서 ON_STOP을 반환합니다.
쉽게 설명해서, STARTED에 수직으로 그려진 선을 기준으로, 선으로 화살표를 향하고 있다면 To, 선에서 화살표가 나오고 있다면 From을 씁니다.
즉, downToStarted -> Started로 선이 위에서 아래로 향하고 있다? -> ON_PAUSE~
upFromStarted -> Started에서 선이 위로 나오고 있다? -> ON_RESUME ~ 과 같습니다.
그래서, repeatOnLifecycle에서 파라미터로 STARTED를 전달해 주었을 때, downFrom을 사용하기 때문에 ON_STOP을 반환합니다.
그리고 나서, 옵저버 등록부분을 보면 이해가 빠릅니다 !
Lifecycle이 ON_START를 호출한 뒤, 옵저버가 등록이 되고, ON_STOP이 불리게 되면 job을 cancel해주게 됩니다.
사실 RepeatOnLifecyle 최상단에 보면 이렇게 써있습니다.
코루틴 안에있는 블럭을 최소 STARTED 상태에서 (STARTED를 썼을때 이야기입니다) 수행한다.
저는 최소라는 말이 이해가 잘 안갔고, 내부 코드를 보니 아래와 같이 정리할 수 있을 것 같습니다. 사진과 함께 다시 보면,
위의 사진에서 STARTED가 되기 위한 최소 조건, 즉 ON_START가 불리고 난 뒤, ON_STOP이 불리고 바로 뒤가 "최소"라는 것입니다.
위에 말했던 것을 다시 잘 생각해보면 이해가 쉬울 겁니다 ! Evenet 호출 뒤에 State가 따라온 다는 것, 즉 ON_START 이벤트 이후, ON_STOP이 불리면서 STARTED가 끝나는 시점!
이를 통해 STARTED를 쓴다고 해서, 무조건 ON_RESUME, ON_PAUSE만 뜻하지 않는 다는 것을 알게 되었습니다.
면접에서 STARTED를 썼다고 해서, ON_RESUME에서 구독하고, ON_PAUSE에서 구독을 취소한다고 하면 안될 것 같습니다.
또한 이를 통해 조심해야 할 것을 한가지 더 알 수 있는데, 바로 lifecyclewOwner와 viewLifecycleOwner입니다.
프래그먼트에서 viewLifecylceOwner를 사용하여 ui 업데이트를 구독 하지 않고, 일반 this, 또는 lifecycleOwner를 사용한다면, 어떻게 될까요?
아래 사진은 Fragment와 Fragment View의 Lifecycle State를 보여줍니다.
Fragment는 add 해주거나, addToBackStack등을 이용하면, onDestroy가 불리지 않을 수도 있습니다. (자세한 내용은 https://seokzoo.tistory.com/6 이곳에서 확인해봅시다 ! )
즉, 이러한 코드들은 LifecycleEvent를 DESTROYED를 타지 않아, 적절한 옵저버 구독 해제가 되지 않을 수 있다는 점입니다.
실제로 viewLifecycle을 이용하지 않은 일반 lifecycleOwner나 this를 이용하여 liveData를 구독한다면, 아래의 사진처럼 currentState가 DESTROYED인 상황이 오지 않을 수도 있다는 것입니다. 즉, removeObserver가 안되고, 계속해서 값을 observe하고 있을 수도 있다는 점! 명심하여 개발하도록 하면 좋을 것 같습니다.
"A SMALL LEAK WILL SINK A GREAT SHIP"
명심하고 개발하면 좋은 개발자가 되는 날이 올겁니다... 반 드 시...