代码
按如下代码写法,在 Activity 发生重建时,如果 observer 被执行到就会出现崩溃
崩溃的堆栈是
java.lang.IllegalStateException: Fragment not attached to a context.
查看 Fragment 代码可以看出该异常是因为 Fragment 已经与 Activity 脱离后,其内部 mHost 字段为空,拿不到 context,在 requireContext 等方法中抛出的。
但 LiveData 是可感知生命周期的,一旦 Activity 被销毁,LiveData 会自动取消监听,理论上不会出现 LiveData 的回调被执行,但 Fragment 的 context 为空。
原因
Activity 在重建(比如旋转屏幕)时会恢复它原来保存的 Fragment,这一步是在 onCreate 方法中发生的,所以正常情况下会先于我们的代码执行。
在 Fragment 被重建出来后,它会自动与 Activity 的生命周期状态拉平。如果我们在 onViewCreated 等方法中向 LiveData 中添加观察者,那么被自动重建的 Fragment 对象就会监听 LiveData。
一旦此时传给 LiveData#observe()* 方法的第一个参数是 requireActivity,那么 LiveData 监听的是 Activity 的生命周期,只要 Activity 没有销毁,该 observer 会一直存在。
然后程序继续执行,假如我们没有处理 Fragment 被自动重建的情况,可能会自己再创建一个 Fragment 实例替换掉被自动重建的实例。此时,相当于我们向 LiveData 中添加了两个观察者。而且第一个 Fragment 已经与 Activity 脱离了,只不过它依旧监听着 LiveData。
现在只在 LiveData 有事件变动,就会导致第一个 Fragment 被执行,而且没有 context,就会发生崩溃。
解决办法
将
requireContext换成viewLifecycleOwner,它会在Fragment被移除时进行销毁状态,从而从LiveData中移除观察者。
当然,也可以在观察者中手动判断 context 是否为空或者在 Fragment 的 onDetach 中手动移除观察者。
扩展
除了 Activity 发生重建时会出现这种异常,也有可能手动将 Fragment 移除时都会出现这种情况。所以 在 Fragment 中使用 LiveData 时一定要使用 viewLifecycleOwner,而不是使用 requireActivity 来代替