【吐血】一次出产环境NPE溃散的排查记载

直接说引起NPE的根本原因(有被自己菜到):

rx订阅没有撤消,回调时Fragment现已被收回,引证view调更新办法,天然NPE。

就这?是的,便是这么简略的根底过失,我排查了一天Android,rx订android什么意思阅要撤消这可是知识:

  • 要么引入生命周期办理;
  • 要么界说接口crc过错计数CompositeSubscriptjava言语ion,java怎样读在Actandroid是什么手机牌子ivity、Fragment毁掉时clear()
  • 要么独自撤消订阅,RxJava2用unsubscribe(),RxJava1用dispose();

我认为的开发知识,却被老项目打脸,接着我来捋一捋工作的来龙去脉,望我们吸取教训,排查BUG时少走弯路,过失原因现已给出了,对排查进程不感兴趣的能够直接略过了~

【吐血】一次出产环境NPE溃散的排查记载


0x1、一差二错解了另一个BUG

昨日早上十一点半+,翻开疼讯视频,准备边看剧边干饭,作用组长钉钉甩来一个:

【吐血】一次出产环境NPE溃散的排查记载

翻开一看,我擦,刚上线的版别,爆了两千多次这个过失:

【吐血】一次出产环境NPE溃散的排查记载

翻开详细日志一看:

【吐血】一次出产环境NPE溃散的排查记载

NPE,空指针反常,调用改写组件的关闭改写办法报空,排查下日志的其他部分,用 自己的写的脚本 去下稠浊,看下是否得数组c言语到更多有kotlin下载帮助的信息。

没有什么卵用,不过奇怪的是,这个BUG的报错数还在不断增加,却没有用户反应这个问题。

集成查验测三android/yunos轮没发现,我们接口crc过错计数自己自测也不复Android现不了。

只能看进行更多的日志排查,一条日志引起我接口测验的留心:

UnknownHostException: Unable to resolve host "xandroid平板电脑价格xx.xxx.com": No address associate接口测验d with hostKotlinname

然后报NPE,指向反常处理里的finishRefresh():

【吐血】一次出产环境NPE溃散的排查记载

em?难不成过失处理的代码有问题?检数组排序验人员人手不行,数组漏掉 网络反常鸿沟 查验也很正常,并且或许都不理解怎样 模仿网络反常和弱网状况

Charkotlin教程les 抓包仿android下载装置照一波,定位到列表接口下断点,央求后,直接把央求给 Abort 掉(丢掉):

【吐血】一次出产环境NPE溃散的排查记载

揭露使用溃散了,心中窃喜,这么快就定位到了问题了?看下过失日志:

【吐血】一次出产环境NPE溃散的排查记载

擦,怎样kotlin下载接口是NPE,而是数组越界,看了下代码,本来是setEmptyV数组iew()后没有android什么意思notifyDataSetChakotlin极简教程nged()一下。

好家伙,没处理BUG,却解了其java难学吗他一个BUG,这…算是因祸得福吗?

【吐血】一次出产环境NPE溃散的排查记载


0x2、真的是KAE的锅android什么意思吗?

NPE的问题还没处理,可是 干饭要紧,下午睡醒再说,控件为null,隐约觉得或许是 kotlin-android-extensions (后边kotlin下载都简称KAE) 的锅,由于曾经也遇到过根据id获取View实例为空的状况,不过那是由于id重复,于是干饭前在群里问了下小伙伴:

【吐血】一次出产环境NPE溃散的排查记载

我们都很热心肠劝我不要用KAE,坑多,官方引荐ViewBinding,手写findViewById稳等,这些android的drawable类我都知道…

【吐血】一次出产环境NPE溃散的排查记载

可是哪能说话就换,项目里那么当地用到了,并且换一个办法,并没有kotlin和java真的处理问题,最少得搞清楚问题发生原因吧…

【吐血】一次出产环境NPE溃散的排查记载

午睡完,持续排查,直接源码看下KAE是怎样让你清除findViewById的,随手写个查验项目,写个TestActivity,里边引证下某个控件,依次点java模拟器Tools → Show Kotlin ByteCode → Decompjava开发ile

【吐血】一次出产环境NPE溃散的排查记载

也是调 Activity.findViewById() 查找控android什么意思件,没缺陷,试试Fragment的状况:

【吐血】一次出产环境NPE溃散的排查记载

和Activity完结办法如出一辙,唯二的差异数组的界说是:

  • ① 调用Fragment的getView()办法的布局 (onC数组函数的使用办法reateView回来的View);
  • ② 重写onDestroyView()办法,清空map中的实例;

你或许有疑问,为啥要在onDestroyView()里清空Map,而不是onDestory()里?

答:考虑到Fragment复用的场景,详细如下:

replace() Fragment 后会履行 onDestoryView(),而不是履行onDestory()彻底毁掉,目的是在毁掉视图的一起,保存View状况和Fragmenandroid的drawable类t成员状况,下次加载时能够直接走onCreateVjavascriptiew(),更快加载以抵达复用的目的。

关于View状况的保存机制,笔者也不是特别了解,大约瞄下TextView的源码(onSaveInstanceStateonRestoreInstanceState),看到了完结Parcelable重写了一些办法,猜想java工作培训班序列化。而序列化和反序列化kotlin面试题前后,方针实例是不相等的,此刻Map里还保存这之前的键值对(id → 实例),此刻根据id拿到的Vi接口的作用ew实例肯定是不对的,所以这儿做了清空操作。

所以这儿也没缺陷,所以不是KAE的锅,虽然这儿不触及,但也数组把Adapter状况怎样findViewById也过一下~

KAE不支持直接在adapter里直接用,需要在bui接口英文ld.gradle增加下述kotlin怎样读实验性配数组c言语置:

// 主要是为了启用LayoutContainerjava难学吗
androidExtensions {
experimental = true
}

而调用办法其实分两种,第一种是这样:

【吐血】一次出产环境NPE溃散的排查记载

看下字节接口crc过错计数码转Jakotlin下载va:

【吐血】一次出产环境NPE溃散的排查记载

直接findViewById的,看看另一种,让ViewHolder完结LayoutContainer的办法:

【吐血】一次出产环境NPE溃散的排查记载

看字节码转Java:

【吐血】一次出产环境NPE溃散的排查记载

原理同样是创立一个hashMap来保存引证,经Android过Vijava工作培训班ewHolder传进的Viewandroid是什么手机牌子进行绑定,真要在adapter里用KAE,建议运用第二种。

在 《Kotlin Android Extensions遭扔掉,官方引荐运接口的作用用ViewBjava开发inding》 一问中说到KAE的问题:

【吐血】一次出产环境NPE溃散的排查记载

除此之外空间换时刻,用一个额外的HashMap来存储View实例,更重要的是这部分内容对大部分运用者而言是黑盒,有时会踩上一些莫名其妙的”坑”。android手机

官方提了一嘴运用Vie数组和链表的差异wBinding代替KAE,其实数组和链表的差异便是启用ViewBinding功用后,AS自动kotlin下载为每个布局文件生成一个对应数组的Binding类,在里边完结View绑接口英文定(包括判空),文件输出目录:/build/ge接口英文nerated/data_binjava面试题ding_base_class_source_out

数组公式体玩法能够参见:《kotlin-android-extens数组去重ions插件也被扔掉了?扶我起来》


0x3、灵光乍现的瞬间

排查完不是KAE的锅,那到底是什么原因导致的控件为空呢?排查发展一会儿陷入了僵局,只能从用户行为下手了,友盟上不知道为何看到不到用户的行为日志。

【吐血】一次出产环境NPE溃散的排查记载

好在有自家全埋点,翻开 Kibana,过滤过失类型日志,找到过失日志,获取deviceid,然后查询用户行为。

【吐血】一次出产环境NPE溃散的排查记载

经过剖析多个用户报错的状况,我发现了一个规则:

都是一次首页的Loading,然后溃散,而间隔用户前次翻开APP的时刻一般都会良久。

脑子里忽然蹦出一个主见,该不会是由于APP被收回,从头翻开Activity重建的问题把,由于APP中有一个跟很多APP相同的鸡接口crc过错计数贼操作,虽然提示了 “再按一次kotlin实战退出程序”,但其实是调用moveTaskToBack(kotlin言语)退到后台罢了。

【吐血】一次出产环境NPE溃散的排查记载

模仿APP被收回就简略了,AS跑下程序,来到出问题的页面,APP退到后台,直接在Logcat把程序接口自动化干掉,接着从头翻开程序,静待片刻,揭露,溃散了,看下日志信息:

【吐血】一次出产环境NPE溃散的排查记载

好家伙,揭露复现了,因java开发为Activity重建导接口的作用致的android/yunos改写控件为空,噼里啪啦跟组长解说一波溃散的原因,然后应急处理android平板电脑价格办法便是调用前先判空,保证不溃散先。

【吐血】一次出产环境NPE溃散的排查记载

本来快到下班的点了(6点),正常状况应该是吃点东西摸鱼等下班了,不过没搞清楚引发这个问题的详细原因androidstudio装置教程,回家也是回忆犹android/yunos新,干脆加班排查下吧。


0x4、加班加点排查

触及Activity重建javascript,那估量也跟Fragment生命周期,Fragment多层嵌套之类的脱不了关连,在BaseActivity和BaseFragment中把生命周期相关的回调都加上日志。

【吐血】一次出产环境NPE溃散的排查记载

页面的话三层嵌套:CustomerFragment → ThirdAgentListFragment → CustomerChildNewFragment

接着模仿溃散,看日志输出作用剖析:

【吐血】一次出产环境NPE溃散的排查记载

不难看出Activity重建的时分把Fragment都康复了,可是很快又销毁掉了,正常来说恢数组和链表的差异复Fragment的流程:

onCreateViewJava() → onViewCreated() → onActivityCreated() → 各种初始化操作

这儿却直接立刻走onDestoryView()也走了onDestory(),发生这个原因其实是replace,看回代码:

【吐血】一次出产环境NPE溃散的排查记载

调用FragmentManager的replace()办法,而正常两个Fragment走的生命周期(未接口调用addToBackStack):

  • 被替换Fragment:onPa接口类型use() → onStop() → onDestroyView() → onDestroy() → onDetach()
  • 替换Fragment:onAttach()数组排序 → onCreate() → onCreateVie接口w() → onViewCreated() →java开发 onActivityCreated() → onStart()数组 → onResume()

所以kotlin下载,这儿的实际逻辑是接口自动化这样:

康复的方数组指针法创立了Fragment → 创立新的Fragment → 替换掉Fragment → 康复创立的Fragment被干掉

然后,我在javascriptFragment的ojava模拟器nActivityCreated()中又发起了一个央求,那就存在一种状况:央求宣告去了,呼应还没回来,Frjava面试题agment就被替换干掉了,这个时分去调现已毁掉的Frjava怎样读agment里的Vandroid/yunosiew实例,妥妥滴空指针啊!

一种看似取巧的处理办法:savedInstanceState(Bundle) 办法中判别参Android数是否为空,不为空就不加载央求:

【吐血】一次出产环境NPE溃散的排查记载

当然,治本的办法肯定是从网络央求下手,当Activity或Fragment毁掉时,需要把rx的订阅都撤消掉,办法便是开始说的几种数组公式

项目都四五年了,竟然一直没爆这个BUG,大约的原因是:

单Activity接口英文、多接口测验Fragment玩法,没有再三的re接口文档place() Fjavaapi中文在线看ragment的场景,并且大部分央求都有不行撤消的Loadinjava面试题g。

排查了一天,本来便是这样一个简略的BUG,前人挖坑,接口后人填坑,真是一口老血…

【吐血】一次出产环境NPE溃散的排查记载

不过在排查进程中也收成不少:

  • 了解KAE不用findViewById的原理,以后能够放心运用了;
  • ViewBinding有个大约了解;
  • 对Fragment生命javascript周期的验证(平常都是死记)android系统
  • 了解了一下Activity详细重建机制;

kotlin和java说这么多,解BUG之路道阻且跻,期接口crc过错计数望本文对你日常的Debug定位过失有所帮助,谢谢~