Fragment 1.3.0-alpha04 부터 FragmentManager
가 FragmentResultOwner
를 구현합니다.FragmentManager
는 requestKey
에 해당하는 bundle
을 등록하고 이를 조회하는 방법으로 Fragment 사이에서 결과를 전달할 수 있게 되었습니다.
특히 FragmentManager
로의 명시적인 참조 없이 Fragment.setFragmentResult()
, Fragment.setFragmentResultListener()
을 통해 현재 Fragment와 상호작용하는 FragmentManager
에 result를 등록하고 Key에 해당하는 결과를 가져올 수 있어서 간단히 이용할 수 있습니다.
공식홈페이지에서는 onCreate()
callback func에서 등록해주고 사용하는 모습을 볼 수 있습니다.
구글에서 제공하는 샘플이니 그대로 사용할 수 있을까요?
일반적으로 key-value 쌍 구조를 구현하는 과정에서, Producer-Consumer 결합도를 낮추기위해 Key를 Producer에 의존하게 구현하는 경우들이 많습니다.
샘플대로라면 Producer base Key 관리 정책을 이용할때 문제점이 발생할 수 있습니다.
어떤 시나리오에서 문제가 발생하는지 예시를 들어보겠습니다.
각각의 이미지는 아래의 플로우를 설명합니다.
1: A→B→C⇒B(resultC)
2: A→C⇒A(resultC)
(각 A, B, C는 Fragment를 의미합니다. 화살표는 Fragment transection을 나타내며, ⇒ 는 result 반환하는 pop back stack transection 입니다.)
Result 전달 과정을 간략하면, ResultFragment 진입 전에 ResultFragment name을 Key로 setFragmentResultListener()
를 호출하며 ResultFragment 클래스는 Key로 setFragmentResult()
를 호출하고 종료합니다.
위의 1,2의 시나리오는 구글 샘플로도 정상적으로 통과합니다.
하지만 다음의 시나리오는 어떻게 동작할까요?
3: A→B→C⇒B(resultC)⇒A→C⇒A(resultC?)
A는 C로부터 결과를 받아오지 못합니다.
FragmentManager는 requestKey를 Map으로 관리한다.
3번 시나리오의 실패 이유는 FragmentManager.setFragmentResultListener()
세부 구현을 살펴 보면 알 수 있습니다.
FragmentManager.setFragmentResultListener()
의 세부구현에는 아래와 같은 코드가 있습니다.
fragmentResultListener
를 등록하는 mResultListeners
는 Map 구조체로, put()
호출 시 동일한 requestKey
로 등록된 이전 listener
를 대체합니다.
3번 시나리오의 경우 B Fragment의 onCreate()
콜백에서 fragmentResultListener
를 대체한 상태기 때문에 A Fragment의 resultListener
가 동작하지 않는 것입니다.
그럼 어떻게해야 반환측에서 사용하는 requestKey
를 유지하면서도 유연한 result 반환을 할 수 있을까요?
onViewCreated()
callback에서 listener 등록하기
간단하게 onCreate()
에서 등록하던 fragmentResultListener
init 시점을 onViewCreated()
로 옮기는 것으로 해결됩니다.
requestKey
를 Map으로 관리한다는 점에서 착안하여 항상 최상단에 위치한 Fragment
의 fragmentResultListener
를 유효하게하면 위의 시나리오 문제를 해결할 수 있습니다.
이 방법과 별개로, frament 전환 과정에서 backstack을 이용하지 않는 방법이 있긴합니다. 다만, fragment result를 위해 fragment navigation에 대한 구현을 강제하는 방법이라 권하지는 않습니다.
(이에 대한 내용은 아래 깃허브에서 좀 더 자세하게 설명합니다.)
정리
Fragment Result API에서 중복된 키를 어떻게 관리하는지 알아봤습니다.
위의 테스트를 직접 해볼 수 있는 깃허브 주소를 공유하면서 포스트를 마무리하겠습니다.
깃허브 프로젝트에는 일부 보완된 설명을 추가했으니 가볍게 읽어 보셔도 도움이 되실 듯 합니다.