I have a fragment where I am using DefaultLifecycleObserver to handle launching a file chooser and getting content (via a uri). The first time my fragment is created in the activity lifecycle, the behavior works fine. If I navigate to a different fragment and come back, the fragment gets recreated, but the ActivityResultLauncher is not re-registered.
I was able to get around the issue by creating a cleanup()
function that manually unregisters the Activity result launcher. I call this cleanup method in the Fragment's onDestroy()
and it fixes the issue. However, this approach feels very hacky. What is the idiomatic way of handling re-registering the ActivityResultLauncher?
class FileSelectorLifecycleObserver(private val registry : ActivityResultRegistry, private val callback: (Uri) -> Unit):
DefaultLifecycleObserver {
private lateinit var getContent: ActivityResultLauncher<String>
override fun onCreate(owner: LifecycleOwner) {
getContent = registry.register("com.histogramo.app.FILE_LOAD", owner, ActivityResultContracts.GetContent()) { uri ->
uri?.let { callback(it) }
}
}
fun cleanup() { getContent.unregister() }
fun selectFile() { getContent.launch("application/octet-stream") }
}
class MyFragment : Fragment() {
private var _binding: MyFragmentBinding? = null
private val binding
get() = _binding
private lateinit var observer : FileSelectorLifecycleObserver
override fun onDestroyView() {
_binding = null
super.onDestroyView()
observer.cleanup()
lifecycle.removeObserver(observer)
}
override fun onCreateView(
inflater: LayoutInflater,
container: ViewGroup?,
savedInstanceState: Bundle?
): View {
_binding = MyFragmentBinding.inflate(inflater, container, false)
observer = FileSelectorLifecycleObserver(requireActivity().activityResultRegistry) { uri ->
// DO STUFF WITH THE FILE
}
lifecycle.addObserver(observer)
return binding?.root ?: View(context)
}
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState)
binding?.loadFileButton?.setOnClickListener {
activity?.let { observer.selectFile() }
}
}
}
You're using
lifecycle.addObserver(observer)
Which is registering the observer with the Fragment's lifecycle
- not with the Fragment's view lifecycle (the one that is created in onCreateView
and destroyed in onDestroyView
). This means it will only get a single onCreate
call when the Fragment's onCreate
is called, not a call every time the fragment's view is created (e.g., onCreateView
is called).
If you want your ActivityResultLauncher to be tied to the Fragment View lifecycle, then you need to use:
viewLifecycleOwner.lifecycle.addObserver(observer)
That will ensure your FileSelectorLifecycleObserver
is called every time the Fragment's view is created, which would be appropriate if you are creating a new Observer in every onCreateView
.