WeTest 导读
遇到了诡异的crash,crash栈和代码分析很难得出crash根本原因,通过反编译apk包,我们找到了crash的根本原因。 一个crash的探索历程,一个程序猿的内心独白!
欢乐斗棋牌3.1.10版本灰度后,出现大量以下类似的crash,用户crash率非常高,粗略计算,超过了30%(后面的统计数据是41%,用户量1886),赶紧还原成旧版本。
本次版本中的MSDK和Bugly有些问题,在某些情况下,crash栈不对,也没有玩家信息,更没有上传游戏自定义日志。
很不幸,上述crash就是这样的。对定位crash造成了很大影响。
按道理,如此高的Crash率,我们自己应该非常容易重现,可是在灰度的一两小时内,发现crash率过高后,我们并没有找到出现条件。
怎么办?好在年后,我们主线版本已经修复了MSDK和Bugly的crash问题,此分支版本因为修改不大,并没有合入,速度合入。
其次,本版本主要代码改动如下:
1. 因政策方面的原因,登录界面上加了一弹窗。
2. 因米大师话费支付需要,更新了米大师SDK,包括jar包和资源。
我们怀疑是登录弹窗引起的问题,但review上述代码,并没有发现问题。另外,米大师相关的调用并不频繁,不可能直接造成30%+的crash率。
分析代码过程中,值得庆幸的是,我们这边出现了一次crash!出现了一次crash!热泪盈眶啊!捞日志分析,推断是选场界面某一种弹窗引起了crash,这大大减少了我们对新加的登录弹窗的怀疑。
可是,代码方面大的修改仅限于上述两个方面,问题出在哪里呢?
MSDK和bugly问题修复版本编译出包后,我们赶紧让测试同学帮忙看下,是否可以复现crash。
测试同学果然给力,在WeTest远程调试机器中发现了以下必现crash。
我们通过分析代码并通过各种尝试,发现自定义的web弹窗必挂。根据上述crash栈,应该是资源错乱引起的。
爽啊!找到必现条件了,本地调试一把吧!我们先编译的Debug版本,可是本地编译的Debug版本并没有crash,换成release版本还是没有crash。一场欢喜一场空!
为什么SODA编译的版本会crash,而本地的Debug和release版本都不crash呢?
后面分两步走:
1.测试同学和小鲜肉开发检查游戏中选场界面中各种可能的自定义web弹窗,看下什么弹窗能够引起如此高的crash。
2. 别的开发同学review自定义web弹窗和资源变化情况。
先是发现活动中心有自定义web弹窗,但活动中心中的页面不可能有这么多人点击,不可能造成如此高的crash率。
后面,测试同学发现老玩家长时间不玩,再玩游戏时,必然弹出以下“专属礼包”,不少机型必挂,这就能解释crash率如此高的原因了。
同时,我们review了自定义WebView的实现,并没有发现资源的任何问题,本版本资源变化如下:
那还原资源修改能否解决问题呢?我们先试着删除unipay_layout_activity_web.xml、unipay_layout_activity_web.xml,SODA构建,安装包还是crash。那就还原所有资源,再试试?还是照样crash,我们就又恢复了资源的修改。
再review了两小时代码后,还是没有从代码中找到问题!内心有点抓狂了,上天帮帮忙吧!我还要留点时间准备答辩啊!无论如何,我想静静。
口干舌燥,先喝点水!不会是SODA抽风了吧?以前SODA偶而会出现一些莫名其妙的编译问题,不管了,清理工作区,再重新编译,也许God help me !
果然,God help me ! 重新编译的安装包,我自测没有问题了,赶紧让测试同学、小鲜肉开发速度测试更多的机型,都没有问题了,后续灰度到外网,此问题已经不再出现。
此问题虽然已经不再出现,可崩溃是什么原因呢?难道以后每次编译都清理工作空间(将代码和编译中生成文件全部删除)?不可能啊,清理工作区间后再编译,编译时间长达1小时(拉取代码占用大部分时间)。
技术人员也有点自己的小追求吧!
我们编译了相同版本号的两个版本,一个会crash,另一个正常。
解压apk包,用Beyond Compare(BC)比较,发现仅以下几个文件有差异,其中MANIFEST.MF、CERT.SF、CERT.RSA的差异都是由classes.dex的差异引起的,也就是说资源本身应该没有问题,这验证了我们上面的推测。
比较classes.dex的二进制,可以看到,差异其实并不大,那差别究竟在什么地方呢?
怎么查看具体的差异呢?
使用dex2jar反编译classes.dex,得到classes-dex2jar.jar,再次用BC比较差异。可以看到仅com.qqgame.hldouniu.web.WebView和com.qqgame.hldouniu.web.c实现存在差异。
使用jd-gui可以查看反编译出来的classes-dex2jar.jar,我们将其复制到文本文件,以便于比使用BC比较,差异如下(左边是crash版本,右边是正常版本):
com.qqgame.hldouniu.web.WebView实现差异如下图所示:
com.qqgame.hldouniu.web.c实现存在差异如下图所示:
看起来仅仅是资源ID不同,正常的版本资源ID比异常版本都加了2。
然而这些资源ID指什么资源呢?
我们找到编译生成的R.java文件,可以看到
unipay_pic_load、unipay_pic_tipsbg_thin是更新米大师SDK时新加入的两张图片
webview_backbtn、webview_failrefresh、webview_loading分别是自定义Web弹窗中后退、失败时刷新、加载的按钮定义。
Crash安装包中的代码用图片来当按钮,造成了自定义Web弹窗的以下crash:
android.graphics.drawable.StateListDrawable cannot be cast to android.graphics.drawable.AnimationDrawable
至此,我们知道了crash的根本原因,但安卓构建过程为什么会有出此问题还待研究(后续有时间再研究)。
为了避免此类问题的出现,后续版本发布我们遵循以下原则:
1. 任一版本发布,即使改动再小都需要重视,代码review、测试覆盖、渠道灰度每个流程步骤都必不可少
2. 版本灰度前或者有资源改动时,都清理下工作区
腾讯WeTest是腾讯游戏官方推出的一站式游戏测试平台,用十年腾讯游戏测试经验帮助广大开发者对游戏开发全生命周期进行质量保障。腾讯WeTest提供:适配兼容测试;云端真机调试;安全测试;耗电量测试;服务器性能测试;舆情监控等服务。
点击地址:http://wetest.qq.com/立即体验!