>问题
我最近注意到一种情况,在Android应用程序中,触发了以下异常:
java.lang.IllegalArgumentException: Cannot add the same observer with different lifecycles
当为视图模型的实时数据注册一些观察者时,在片段的onViewCreated
回调(例如片段 A)中,就会发生这种情况:
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
...
myViewModel.myLiveData.observe(viewLifecycleOwner, Observer(::onValueChanged))
...
}
这让我发现这发生在一种特定情况下,当在活动的onCreate
中收到意图并决定导航到另一个片段(例如片段 B)时。
override fun onCreate(savedInstanceState: Bundle?) {
if ("myAction" == intent?.action) {
if (R.id.fragmentA == navController.currentDestination?.id) {
navController.navigate(FragmentADirections.actionFragmentAToFragmentB())
}
}
}
如果用户从该片段 B 导航回片段 A,则会抛出该IllegalArgumentException
。
调查
在记录生命周期方面发生的事情后,我意识到片段 A 的生命周期状态viewLifecycleOwner
当片段为 LiveData 注册观察者时INITIALIZED
。 这与文档建议的内容相符:
。然后,getViewLifecycle OwnerLiveData() 使用与片段视图对应的新初始化的 Lifecycle Owner 进行更新。此时也会调用 onViewCreated() 生命周期回调。
这是设置视图初始状态的合适位置,开始观察 LiveData 实例,这些实例的回调更新了片段的视图......
但是,当活动从其onCreate
回调控制导航时,片段视图的生命周期永远不会达到STARTED
状态,也不会在显示片段 B 时向下DESTROYED
。因此,从片段 B 返回到片段 A,以前的生命周期所有者观察 LiveData,片段的新生命周期所有者也尝试观察它,引发异常。
[编辑] 我一直试图找出可能触发此异常的更改。我使用androidx.lifecycle:lifecycle-viewmodel-ktx
的版本2.5.1
和2.6.0
运行代码,我发现异常是由版本2.6.0
(和2.6.1
)触发的,而不是版本2.5.1
触发的。我还在片段回调中添加了日志,发现两个版本都调用了onViewCreated
:
onCreateView: Fragment view is INITIALIZED
onViewCreated: Fragment view is INITIALIZED
onDestroyView: Fragment view is INITIALIZED
[/编辑]
当用户"手动"导航到另一个片段并且片段视图的生命周期从INITIALIZED
到CREATED
向下到DESTROYED
时,我还记录
问题
出现了几个问题:生命周期能否从INITIALIZED
直接变为DESTROYED
状态?还是我这边没有向下走的错误?
如果不是,正确的做法是什么?一旦生命周期达到CREATED
状态,是否应该稍后观察 LiveData?
还是活动不应该这么早导航到另一个片段?它是否应该等待片段显示视图后再导航(显然它不适合用户体验)?
我不认为这是一个明确的答案,但我看到了解决问题的两种方法。
一种是将喷气背包生命周期库降级,特别是androidx.lifecycle:lifecycle-viewmodel-ktx
,以2.5.1
。
另一种是从活动的onStart
回调导航到其他片段。