Duang~ Android堆栈慘遭毁容?精神哥揭露毁容真相!
2015-05-30 16:49
387 查看
最近精神哥接到多个小伙伴的投诉,说无法看懂Bugly崩溃克星页面上显示的堆栈信息!精神哥赶紧把正研究的Top Crash崩溃和心爱的鸡爪放下,开始着手跟进。经分析发现,大家误会Bugly崩溃克星了,这一切都是Proguard搞的鬼!
下面请容精神哥一一道来!
· 类名(Full ClassName,红线标注);
· 方法名(Method,蓝线标注);
· 源文件及行号(SourceFile:LineNum,绿线标注)。
我们再看看图二显示的异常堆栈内容,可以发现栈中的某一调用帧(Frame),一个类名下会存在多个方法名,而且第一个方法尾部的源文件及行号是(UnknownSource),根本看不出源文件和行号。
但,有经验的同学童鞋应该能看出来,这个堆栈是被Proguard还原过的!
怎么看?很简单,出错时JVM生成的堆栈中每一个"方法描述",只有“方法名”而没有“方法返回类型”及“方法参数”。但被Proguard还原过的堆栈,应该有“方法返回类型”及“方法参数”。
那么这里有两个问题:
1. 为什么堆栈会是Unknown Source?是Bugly崩溃克星没有上报吗?
2. 为什么com.xx.a.a会被还原成多个方法(图2示例中就被还原了5个方法)?
精神哥继续给大家分析分析!
有经验的童鞋应该会发现,开发阶段上报Bugly的Crash崩溃堆栈都是有源码及行号的,但发布后就变成了Unknown Source,为什么?
如图3所示,我们发布时源码信息会先经过javac编译,再经过proguard混淆,才被打包进发布的apk中,最终Crash崩溃后Bugly获取到的堆栈中有木有源码及行号就要看这两步了。
所以想让Crash崩溃堆栈不再Unknown Source,需要两个保证:
源文件名、行号、变量名称,都存在class文件的debug信息中,javac编译时可以选择是否保留debug信息,那么我们肯定是要保留的!下面是使用不同编译方法时保存源文件名及行号的解决方法示例。
l
命令行javac编译的解决方法
l
使用Ant编译的解决方法
l
使用Eclipse编译的解决方法
那么问题来了:假如堆栈中有
因为它可以是
被还原成
图二中的5个方法也就是这么来的。
真不能怪Proguard还原工具,谁让Java堆栈中不给出方法的完整描述或签名(返回值,方法名,方法参数),而只有方法名,但Java语法又允许方法名相同。但这是有解决办法的!
Proguard还原工具其实还能根据行号进行区分,如果你的堆栈已经解决了UnknownSource问题,那么你的还原Mapping文件就不一样了。如图7,大家看到mapping文件中多了“数字:数字”这类内容,这个就是行号,也就是说
假如堆栈中有
那么堆栈问题就没了吗?有人曾问我:堆栈里显示A方法调用了D方法,跟着D方法崩溃了,但实际查看代码A并没有调用D方法啊?
精神哥汗了,不知道Progurad会做代码优化的么?认真看看是不是有A->B->C->D ?如果有!那是因为Proguard大神很喜欢把你没用的->B->C给干掉,直接A->D了!
本文系腾讯Bugly特邀文章,转载请注明作者和出处“腾讯Bugly(http://bugly.qq.com)”
下面请容精神哥一一道来!
问题描述
先看看图一中显示的正常堆栈内容,正常堆栈中每一个调用帧(Frame),都会有3个元素组成:· 类名(Full ClassName,红线标注);
· 方法名(Method,蓝线标注);
· 源文件及行号(SourceFile:LineNum,绿线标注)。
我们再看看图二显示的异常堆栈内容,可以发现栈中的某一调用帧(Frame),一个类名下会存在多个方法名,而且第一个方法尾部的源文件及行号是(UnknownSource),根本看不出源文件和行号。
但,有经验的同学童鞋应该能看出来,这个堆栈是被Proguard还原过的!
怎么看?很简单,出错时JVM生成的堆栈中每一个"方法描述",只有“方法名”而没有“方法返回类型”及“方法参数”。但被Proguard还原过的堆栈,应该有“方法返回类型”及“方法参数”。
那么这里有两个问题:
1. 为什么堆栈会是Unknown Source?是Bugly崩溃克星没有上报吗?
2. 为什么com.xx.a.a会被还原成多个方法(图2示例中就被还原了5个方法)?
精神哥继续给大家分析分析!
为什么堆栈会是UnknownSource?
是Bugly崩溃克星忘记上报了吗?不是!那是因为,你代码编译的姿势不对!有经验的童鞋应该会发现,开发阶段上报Bugly的Crash崩溃堆栈都是有源码及行号的,但发布后就变成了Unknown Source,为什么?
如图3所示,我们发布时源码信息会先经过javac编译,再经过proguard混淆,才被打包进发布的apk中,最终Crash崩溃后Bugly获取到的堆栈中有木有源码及行号就要看这两步了。
所以想让Crash崩溃堆栈不再Unknown Source,需要两个保证:
保证一:javac编译保留源文件名及行号
【TODO】javac编译保留源文件名及行号源文件名、行号、变量名称,都存在class文件的debug信息中,javac编译时可以选择是否保留debug信息,那么我们肯定是要保留的!下面是使用不同编译方法时保存源文件名及行号的解决方法示例。
l
命令行javac编译的解决方法
javac -g:{lines,source} XXX.java
l
使用Ant编译的解决方法
l
使用Eclipse编译的解决方法
保证二:Proguard混淆中保留原文件名及行号
【TODO】Proguard中keep住源文件及行号-keepattributes SourceFile,LineNumberTable
为什么com.xx.a.a被还原成多个方法了?
如图六,大家看到Class1_Promoted类下面的两个方法都被混淆成了a方法。
那么问题来了:假如堆栈中有
proguard.retrace.a.a(Unknown Source),应该被还原成神马呢?
因为它可以是
proguard.retrace.Class_Promoted类下的方法
void doClass1(int),也可以是
void doClass1()。所以Proguard还原工具直接把这两个都给你列出来都作为你的还原结果,所以:
proguard.retrace.a.a(Unknown Source)
被还原成
proguard.retrace.Class_Promoted. void doClass1(int) void doClass1()
图二中的5个方法也就是这么来的。
真不能怪Proguard还原工具,谁让Java堆栈中不给出方法的完整描述或签名(返回值,方法名,方法参数),而只有方法名,但Java语法又允许方法名相同。但这是有解决办法的!
Proguard还原工具其实还能根据行号进行区分,如果你的堆栈已经解决了UnknownSource问题,那么你的还原Mapping文件就不一样了。如图7,大家看到mapping文件中多了“数字:数字”这类内容,这个就是行号,也就是说
proguard.retrace.a类中24行到26行属于
void doClass(int)方法 ,29到31行属于
void doClass1()方法。
假如堆栈中有
proguard.retrace.a.a(Demo.java:25),就知道要被还原成
proguard.retrace.Class_Promoted.voiddoClass1(int)了。
精神哥总结
Proguard作为非常优秀的工具,让我们安装包体积变小了,代码混淆更安全了,代码裁剪优化速度更快了,但确实也埋下了一些坑,增加了我们定位崩溃时的成本。跟着精神哥的这篇文章把原文件名和行号补上后,绝大部分的堆栈问题都可以被解决了!那么堆栈问题就没了吗?有人曾问我:堆栈里显示A方法调用了D方法,跟着D方法崩溃了,但实际查看代码A并没有调用D方法啊?
精神哥汗了,不知道Progurad会做代码优化的么?认真看看是不是有A->B->C->D ?如果有!那是因为Proguard大神很喜欢把你没用的->B->C给干掉,直接A->D了!
本文系腾讯Bugly特邀文章,转载请注明作者和出处“腾讯Bugly(http://bugly.qq.com)”
相关文章推荐
- Android开发中的MVC设计模式
- Android PullToRefresh (ListView GridView 下拉刷新) 使用详解
- Android 自定义ScrollView ListView 体验各种纵向滑动的需求
- android项目中错误 java.lang.NoClassDefFoundError
- Android大牛们的博客
- Android——showAsAction()
- android无标题全屏的三种设置方式
- android中的显式与隐式Intent
- Android之在Layout中自定义View
- 教你搞定Android自定义View
- Android实战简易教程-第四枪(ScrollView和HorizontalScrollView动态添加控件并提供事件监听)
- Android自定义Layout
- Android开发-将自定义View布局到Layout中并调用
- Android学习笔记之自定义Toast
- 全国首发 | 腾讯Bugly携手极客学院,「Android Crash 跟踪方法」强势上线!
- Android自定义View onMeasure
- Android学习笔记之Fragment的两种使用方法
- android:layout_weight为0
- Android 5.0 来了,你的应用准备好了么?
- Android(java)学习笔记95:Android原理揭秘系列之View、ViewGroup