rx订阅要取消这可是常识:
- 要么引入生命周期管理;
- 要么定义CompositeSubscription,在Activity、Fragment销毁时clear()
- 要么单独取消订阅,RxJava2用unsubscribe(),RxJava1用dispose();
我以为的开发常识
,却被老项目打脸,接着我来捋一捋事情的前因后果,望大家吸取教训,排查BUG时少走弯路,错误原因已经给出了,对排查过程不感兴趣的可以直接略过了~
0x1、阴差阳错解了另一个BUG
组长钉钉甩来一个:
打开一看,刚上线的版本,爆了两千多次这个错误:
打开详细日志一看:
NPE,空指针异常,调用刷新组件的关闭刷新方法报空,排查下日志的其他部分,用 自己的写的脚本去下混淆,看下是否得到更多有帮助的信息。
没有什么卵用,不过奇怪的是,这个BUG的报错数还在不断增加,却没有用户反馈这个问题。
集成测试测三轮没发现,我们自己自测也不复现不了。
只能看进行更多的日志排查,一条日志引起我的注意:
UnknownHostException: Unable to resolve host "xxx.xxx.com": No address associated with hostname
然后报NPE,指向异常处理里的finishRefresh():
em?难不成错误处理的代码有问题?测试人员人手不够,漏掉 网络异常边界 测试也很正常,而且可能都不懂怎么 模拟网络异常和弱网情况。
用 Charles 抓包模拟一波,定位到列表接口,下断点,请求后,直接把请求给 Abort 掉(丢弃):
果然应用崩溃了,心中窃喜,这么快就定位到了问题了?看下错误日志:
擦,怎么不是NPE,而是数组越界,看了下代码,原来是setEmptyView()后没有notifyDataSetChanged()一下。
好家伙,没解决BUG,却解了另外一个BUG,这...算是因祸得福吗?
0x2、真的是KAE的锅吗?
NPE的问题还没解决,控件为null,隐隐觉得可能是 kotlin-android-extensions (后面都简称KAE) 的锅,因为以前也遇到过根据id获取View实例为空的情况,不过那是因为id重复,于是干饭前在群里问了下小伙伴:
大家都很热情地劝我不要用KAE,坑多,官方推荐ViewBinding,手写findViewById稳等,这些我都知道...
但是哪能说话就换,项目里那么地方用到了,而且换一个方式,并没有真的解决问题,起码得搞清楚问题发生原因吧...
继续排查,直接源码看下KAE是怎么让你免去findViewById的,随手写个测试项目,写个TestActivity,里面引用下某个控件,依次点击 Tools → Show Kotlin ByteCode → Decompile
:
也是调 Activity.findViewById() 查找控件,没毛病,试试Fragment的情况:
和Activity实现方式如出一辙,唯二的区别是:
- ① 调用Fragment的getView()方法的布局 (onCreateView返回的View);
- ② 重写onDestroyView()方法,清空map中的实例;