赞
踩
首页和问题反馈重复切换两次就闪退
(因为是公司内部app,原有视频不做展示)
app是原生android studio开发的,部分页面是h5开发的,通过WebView和addJavascriptInterface接口实现js与java的交互
1.由于部分页面是h5开发的,我从代码里直接修改对应的html的代码,比如我在账号的label标签后面加个1,再真机调试,结果发现一点变化都没有,并且从全局搜索里也搜不出登录页面有其他代码,这时候很懵觉得不可能会不生效啊
2.后来同事跟我说修改的html代码要打成zip压缩包放到工程目录里,再打包,app在运行的时候会自动解压,这时候我才知道为什么我无法搜索出登录页面其他相关代码,并且修改了也没生效的原因,于是我照做了,并且成功生效了。有必要记录一下
可以看到在进入问题反馈的页面的时候,会每隔一秒钟打印一段日志,说明有每秒秒执行一次的线程在不断运行(里面部分中文输出是我加的,因为是先解决完问题再做的记录)
紧接着当我切换页面到首页的时候,直接就报空指针异常了,当时我猜测跟这个定时线程肯定关系,因为我是秉着先理解问题再解决问题的心态,所以决定一步步搞懂问题是如何发生的。
因为是在页面切换的时候,发生闪退的,所以要从页面切换的时候寻找问题,首先要搞懂页面是如何切换的
1.点击首页的时候通过调用MrCar_PageJump跳转到并且传参home.htm,如下图:
2.MrCar_PageJump:由于不是web方式所以执行else的_ng_transferAPPMethod,如下图:
3._ng_transferAPPMethod:通过调用window.Android.AndroidTransfer进行页面跳转,这个应该是java注入到js里的一个window对象,供js调用java代码用的,如下图:
4.AndroidTransfer:回到java代码里确实看到有这个方法,并且对应的case调用了PageJump方法如下图:
5.PageJump:打断点跟踪到这里发现这才是真正的跳转地方,先是启动了个新的loginActivity,再把当前的loginActivity结束掉(java层大部分页面代码都是写在loginActivity里的),而具体打开的html页面还要根据参数里的jumpUrl决定(jumpUrl在步骤1就指定了),如下图:
到此搞懂了页面直接是如何跳转的,只是方便我之后排查问题的时候更好的理解代码
在观察日志的时候有说到问题反馈页面有个定时线程,我猜测问题就处在这个定时线程,所以跟踪下这个线程的执行流程,这里踩坑过程就不记录了,直接记录跟踪结果了
1.页面载入的时候执行PageInit初始化
2.PageInit调用GetDataFormAPP方法,回调方法是GetUserInfosCallback
3.GetUserInfosCallback调用了GetUserLocation方法
4.GetUserLocation里由于判断不是web模式,所以执行了MrCar_TransferAPPMethod方法,回调是GetCurrentLocationSucc
5.GetCurrentLocationSucc中调用了GetStationInfosByPhone
6. GetStationInfosByPhone调用的java层的方法,路由参数是MrCarStartRssi
7.java层的case调用了,dispatcher的startLteInfoWithPermissionCheck
8.startLteInfoWithPermissionCheck调用了loginactivity重写的startLteInfo
9.startLteInfo调用了DoubleRSSITest类的exec方法
10.成功的找到了这个每秒执行一次的定时线程,每秒调用一次handler.sendEmptyMessage方法
11.提供了stop方法,以便外界结束这个定时线程
12.分析handler,在DoubleRSSITest的构造器里handler = new MyHandler,是一个继承关系的自定义handler
重写了handleMessage方法,当收到消息后处理完消息,通过sendBroadcast方法将消息分发出去
loginActivity先是注册了广播关键字my_local_broadcast,所以handler的广播消息会被转发到loginActivity的receiver里
ok,到这里就基本知道了这个定时线程的由来以及工作模式了,并且通过单词和注释知道了这个线程是在每秒刷新手机信号
其实闪退的时候日志里的保存信息,有指向到这个receiver里
就是因为这个切换页面的时候,phoneManger为null了,导致调用不到方法抛异常
于是我猜测页面切换的时候,有代码把phoneManager置为null了,结果我全局搜索并没有相关代码,所以肯定是别的原因为null了
接着分析,既然phoneManager不是人为的为null,肯定是自然为null,那么只有一种可能phoneManager对象在切换页面的时候被回收了,根据之前的页面跳转断点分析得知,跳转后会启动一个新的activity,然后当前的activity会执行finish方法,这个就会导致当前的activity里面的变量被销毁,从而导致这个定时线程收到消息后分发到revice里,再次访问phoneManager就会访问不到,最终导致报错闪退!binggo!脑子里瞬间就能把报错的过程推算出来了,就像看到了案发现场从而联想到作案手法!
但是为什么activity都finish了,后台线程怎么会还在执行呢,经过分析,切换页面的时候确实执行了stop方法,线程应该停止,按理来说,不会发生这种情况的
后来经过复盘整个执行过程,页面切换的流程应该是:
Finish(销毁变量)-> revice(消息回调)-> stop(停止发送)
说白了就是stop完了,在stop之前消息就已经到达了,但是变量又被finish销毁了,所以最终报空指针异常。整个流程一步步跟踪下来直到找到问题很有成就感
既然找到了问题,肯定就要解决问题,试过把stop放在onDestroy的第一句代码调用依然会有几率会报错,因为stop只是改变布尔变量值,并不能立即强制结束线程。所以只能在消息回调里做处理,在recever里加个判空判断,这样子在页面销毁后的最后一次消息回调就不会报错,因为被判空了,从而解决问题
前面分析了这么多代码,最后一个判空判断就搞定了,想想还挺搞笑的,但是没有前面的分析作为基础,我不一定能定位到这个地方,而且我是秉着先理解问题再解决问题的心态完成这件事的,这样才能学到东西
(因为是公司内部app,原有视频不做展示)
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。