英文:
ActivityResultLauncher not re-registered after Fragment recreated
问题
我有一个片段,我正在使用DefaultLifecycleObserver来处理启动文件选择器并获取内容(通过uri)。在活动生命周期中创建我的片段的第一次时,行为正常。如果我导航到不同的片段然后返回,片段会被重新创建,但ActivityResultLauncher不会重新注册。
我通过创建一个cleanup()
函数来解决这个问题,该函数手动取消注册ActivityResultLauncher。我在Fragment的onDestroy()
中调用此清理方法,它可以解决问题。但是,这种方法感觉非常不规范。处理重新注册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 ->
// 使用文件执行操作
}
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() }
}
}
}
英文:
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() }
}
}
}
答案1
得分: 1
你尝试过将启动器直接注册为片段的私有属性,而不是将其包装在专用的LifecycleObserver
中吗?这可能会避免手动注册/取消注册和/或清理LifecycleObserver
的需要,这可能是此处错误的根源。
官方文档中有一个示例:
https://developer.android.com/training/basics/intents/result#launch
英文:
Have you tried directly registering the launcher as a fragment's private attribute instead of wrapping it in a dedicated LifecycleObserver
?
This might avoid the need to deal with manually register/unregister and/or cleanup the LifecycleObserver
, which might be the source of the bug here.
There is an example in the official documentation:
https://developer.android.com/training/basics/intents/result#launch
答案2
得分: 1
你正在使用
lifecycle.addObserver(observer)
这会将观察者注册到 Fragment 的 lifecycle
,而不是注册到 Fragment 的 view 生命周期(在 onCreateView
中创建并在 onDestroyView
中销毁的生命周期)。这意味着它只会在调用 Fragment 的 onCreate
时得到一次 onCreate
调用,而不是在每次 fragment 的视图创建时(例如,调用 onCreateView
)都会得到调用。
如果你希望你的 ActivityResultLauncher 与 Fragment 的视图生命周期关联,那么你需要使用:
viewLifecycleOwner.lifecycle.addObserver(observer)
这将确保你的 FileSelectorLifecycleObserver
在每次 Fragment 的视图创建时都会被调用,如果你在每个 onCreateView
中创建新的观察者,这将是合适的。
英文:
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
.
通过集体智慧和协作来改善编程学习和解决问题的方式。致力于成为全球开发者共同参与的知识库,让每个人都能够通过互相帮助和分享经验来进步。
评论