记一次使用android studio分析app闪退原因的过程

您所在的位置:网站首页 安装闲鱼APp 记一次使用android studio分析app闪退原因的过程

记一次使用android studio分析app闪退原因的过程

2023-03-12 01:12| 来源: 网络整理| 查看: 265

闪退演示

首页和问题反馈重复切换两次就闪退

(因为是公司内部app,原有视频不做展示)

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 2018-2019 办公设备维修网 版权所有 豫ICP备15022753号-3