OnBackDispatcher AddCallback이 여러 개가 한 번에 등록되는 문제에 봉착했다.
Activity에서 AddCallback을 통해서 OnBackDispatcher호출시마다 특정 콜백을 등록해 주었지만,
몇몇 Fragment에서 필요한 OnBackPressed콜백을 등록해 쓰면서 문제가 생겼다.
문제
1. WebView 프래그먼트에서 뒤로가기를 누를 시에, 이전 url화면으로 이동하도록 구현,
2. 중간에 다른 Activity를 실행 후 기존 Activity로 돌아오면,
3. WebView프래그먼트에서 등록한 Callback이 사라지는 현상
OnBackDispatcher
위 문제를 파악하다 보니, OnBackPressedDispatcher.kt가 어떤 식으로 동작하는지 이해가 필요했다.
기본적으로 OnBackPressedDispatcher.kt 파일에서 코드를 확인해 보니,
override fun onStateChanged(
source: LifecycleOwner,
event: Lifecycle.Event
) {
if (event === Lifecycle.Event.ON_START) {
currentCancellable = addCancellableCallback(onBackPressedCallback)
} else if (event === Lifecycle.Event.ON_STOP) {
// Should always be non-null
currentCancellable?.cancel()
} else if (event === Lifecycle.Event.ON_DESTROY) {
cancel()
}
}
//onbackpressedDispatcher.kt
위처럼 Lifecycle이 OnStart일때 Callback이 등록되고 OnStop일 때 CallBack이 취소되는 것을 확인할 수 있었다.
또한, CallBack은 ArrayDeque에 저장되어 관리되었으므로, Stack처럼 꺼내 사용됨을 이해했다.
class OnBackPressedDispatcher constructor(
private val fallbackOnBackPressed: Runnable?,
private val onHasEnabledCallbacksChanged: Consumer<Boolean>?
) {
private val onBackPressedCallbacks = ArrayDeque<OnBackPressedCallback>()
private var inProgressCallback: OnBackPressedCallback? = null
private var onBackInvokedCallback: OnBackInvokedCallback? = null
private var invokedDispatcher: OnBackInvokedDispatcher? = null
private var backInvokedCallbackRegistered = false
private var hasEnabledCallbacks = false
//onbackpressedDispatcher.kt
프래그먼트와 액티비티 생명주기
기본적으로 Fragment와 그 프래그먼트를 호스팅 하는 Activity는 대부분 다음처럼 생명주기를 가져간다고 알고 있다.
하지만, 알쏭달쏭했던 문제를 해결하기 위해서 프래그먼트와 액티비티의 생명주기를 이해해야만 했는데, 아래 1번과 2번의 생명주기는 과연 어떻게 될까? (2번 액티비티를 실행했을 때 1번 액티비티를 finish 하지 않은 상태이다)
1번은 책이나 블로그들에서 많이 찾을 수 있을만큼 일반적으로 fragment의 생명주기부터 onStop으로 바뀐 뒤에 Activity의 생명주기가 onStop으로 변화한다.
하지만 2번은? 어떻게 될까? 당연히 Activity부터 생명주기가 시작될거라고 생각했지만, Fragment부터 생명주기가 시작된다.
그래서 왜 문제가 발생?
위에서 살펴 보았듯이 OnBackPressedDispatcher가 OnStart의 생명주기 일 때마다 CallBack이 추가가 되므로, 나의 경우에는 Fragment Specific Callback이 등록이 먼저 되었고, 이후에 Activity에 존재하는 일반적인 Fragment Callback 등록 함수가 함수를 각각 등록하게 되었음을 알 수 있었다.
내가 원하던 것은 Fragment에서 등록한 특정 Callback인데 이를 제대로 사용하지 못했다. (Callback이 맨 위에 저장된 것이 물리적 BackButton을 눌렀을 때 작동함)
해결책
해결책으로는, 2가지 정도가 있을것 같다.
1. Activity에 일반적인 Fragment CallBack을 등록 시에, 내가 따로 만들고 싶은 Callback을 가진 Fragment는 Activity - AddCallBack에서 제외한다.
2. Fragment에서 등록하고자 하는 CallBack을 Fragment Lifecycle - OnResume일 때 등록한다 이다.
하지만, 2번 해결책은 OnBackPressedDispatcher 코드에서 볼 수 있듯이, OnStart일때 추가하는 코드 외에는 다른 코드를 확인할 수 없어. OnResume일 때 CallBack이 항상 추가가 된다는 보장을 할 수 없을 것 같다. 그렇기 때문에, 1번 해결책인 Activity에서 추가하는 일반적인 Fragment Callback 추가 함수에서 특정 Fragment 만 Callback등록을 하지 않도록 하고, 특정 Fragment에서 원하는 Callback을 OnStart생명주기에 맞추어 작성해 주면 될 것이다.
결론
즉, 다시 말하면 MainActivity와 Fragment에서 등록한 Callback들의 순서가 라이프 사이클 호출 순서에 의해 변경이 되었고, 이로 인해 문제가 발생했음을 알 수 있었다.
최근댓글