页面的话三层嵌套:CustomerFragment → ThirdAgentListFragment → CustomerChildNewFragment
接着模拟崩溃,看日志输出结果分析:
不难看出Activity重建的时候把Fragment都恢复了,但是很快又销毁掉了,正常来说恢复Fragment的流程:
onCreateView() → onViewCreated() → onActivityCreated() → 各种初始化操作
这里却直接马上走onDestoryView()也走了onDestory(),发生这个原因其实是replace,看回代码:
调用FragmentManager的replace()方法,而正常两个Fragment走的生命周期(未调用addToBackStack):
- 被替换Fragment:onPause() → onStop() → onDestroyView() → onDestroy() → onDetach()
- 替换Fragment:onAttach() → onCreate() → onCreateView() → onViewCreated() → onActivityCreated() → onStart() → onResume()
所以,这里的实际逻辑是这样:
恢复的方式创建了Fragment → 创建新的Fragment → 替换掉Fragment → 恢复创建的Fragment被干掉
然后,我在Fragment的onActivityCreated()中又发起了一个请求,那就存在一种情况:请求发出去了,响应还没回来,Fragment就被替换干掉了,这个时候去调已经销毁的Fragment里的View实例,妥妥滴空指针啊!
一种看似取巧的解决方式:savedInstanceState(Bundle) 方法中判断参数是否为空,不为空就不加载请求:
当然,治本的方法肯定是从网络请求入手,当Activity或Fragment销毁时,需要把rx的订阅都取消掉,方法就是开头说的几种。
项目都四五年了,竟然一直没爆这个BUG,大概的原因是:
单Activity、多Fragment玩法,没有频繁的replace() Fragment的场景,而且大部分请求都有不可取消的Loading。
排查了一天,原来就是这样一个简单的BUG,前人挖坑,后人填坑,真是一口老血...
不过在排查过程中也收获不少:
- 了解KAE不用findViewById的原理,以后可以放心使用了;
- ViewBinding有个大概了解;
- 对Fragment生命周期的验证(平时都是死记);
- 了解了一下Activity具体重建机制;
就说这么多,解BUG之路道阻且跻,希望本文对你日常的Debug定位错误有所帮助