LiveData 与 IllegalStateException

175 阅读2分钟

代码

按如下代码写法,在 Activity 发生重建时,如果 observer 被执行到就会出现崩溃

image.png

崩溃的堆栈是

java.lang.IllegalStateException: Fragment not attached to a context.

查看 Fragment 代码可以看出该异常是因为 Fragment 已经与 Activity 脱离后,其内部 mHost 字段为空,拿不到 context,在 requireContext 等方法中抛出的。

LiveData 是可感知生命周期的,一旦 Activity 被销毁,LiveData 会自动取消监听,理论上不会出现 LiveData 的回调被执行,但 Fragmentcontext 为空。

原因

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 中移除观察者。

image.png

当然,也可以在观察者中手动判断 context 是否为空或者在 FragmentonDetach 中手动移除观察者。

扩展

除了 Activity 发生重建时会出现这种异常,也有可能手动将 Fragment 移除时都会出现这种情况。所以 Fragment 中使用 LiveData 时一定要使用 viewLifecycleOwner,而不是使用 requireActivity 来代替