一个nullPointer bug的修复
2016-04-07 16:07
561 查看
查看友盟的一个null pointer crash log,日志如下:
但是,解决问题不能靠第一反应,要找下问题的真实原因。现在有四个疑问,
1.crash是由于expandableLabelString是null还是空字符串造成的呢?
2.expandableLabelString为何为null?
3.如何复现问题?
4.如何修复?
一、第一个问题
1.首先看下Html.from()的源码:
2.调用了其重写方法,源码如下:
其中,source(也就是expandableLabelString)通过HtmlToSpannedConverter类的构造方法传入HtmlToSpannedConverter的对象中,赋值给mSource对象句柄,并在converter.convert()方法中调用。
3.看下convert()方法
二、看第二个问题:expandableLabelString为何为null
1.首先看下expandableLabelString的变量声明:
2.再看下expandableLabelString的变量赋值处,只有一处:
3.再看expandableLabelString的调用处,是在页面详情接口(网络请求,api)的回调中,那么expandableString为null,只有可能是接口数据未返回前,用户就进行了点击!这在网络情况不好或者服务端压力大时,是有可能出现的。
三、如何复现这个bug
为了验证自己的推论,在expandableString的赋值语句前加入断点,准备让网络请求结果处理中断,此时点击(执行onClick()),应该可以复现。
但是,执行到断点处时,发现app停留在上个页面,还没跳转到出现问题的页面(调用接口在onCreate()中,接口返回时间很短),这时想起来网络请求虽然是异步的,但结果处理是在UI线程里的,这就执行到UI线程里的断点了,相当于把UI线程阻塞了,此时页面既不能继续渲染,也无法执行任何点击操作。
因此,要将断点设置在网络请求的线程里,执行到断点,进行点击(调用onClick()),果然崩溃,堆栈信息与友盟报出的一致。
四、修复
原因定位清楚,问题修复就简单了,思路两个,一个是将expandableLabelString初始化为空字符串;另一个是,在网络接口数据返回前,不允许用户进行点击操作。前一个思路实现起来简单,后一个思路更加合理。
五、这个问题的总结:
1.对象句柄是否要初始化为默认值,有没有可能出现nullPointer的问题;
2.断点如果在UI线程里,UI线程就被阻塞了,此时既无法进行页面渲染,也无法响应用户点击;断点在非UI线程里,不会阻塞页面渲染的;
3.网络请求过程中,是否应该允许用户点击;
4.修复crash,一定要找清楚原因,不能头疼医头,脚疼医脚。
java.lang.NullPointerException at java.io.StringReader.<init>(StringReader.java:47) at android.text.HtmlToSpannedConverter.convert(Html.java:445) at android.text.Html.fromHtml(Html.java:139) at android.text.Html.fromHtml(Html.java:102) at com.liveaa.education.ExerciseBookDetailActivity.onClick(ExerciseBookDetailActivity.java:386) at android.view.View.performClick(View.java:4438) at android.view.View$PerformClick.run(View.java:18439) at android.os.Handler.handleCallback(Handler.java:733) at android.os.Handler.dispatchMessage(Handler.java:95) at android.os.Looper.loop(Looper.java:136) at android.app.ActivityThread.main(ActivityThread.java:5095) at java.lang.reflect.Method.invokeNative(Native Method) at java.lang.reflect.Method.invoke(Method.java:515) at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:786) at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:602) at dalvik.system.NativeStart.main(Native Method)出错语句在onClick()里,代码很简单:
tv_label.setText(Html.fromHtml(expandLabelString));看到crash 堆栈层次顶部里有Html.from()里,判断应该是expandLabelString为null或者空字符串造成的。第一反应是使用TextUtils.isEmpty(expandLabelString)进行判空,如果不为空,再执行Html.fromHtml(expandLabelString)语句。
但是,解决问题不能靠第一反应,要找下问题的真实原因。现在有四个疑问,
1.crash是由于expandableLabelString是null还是空字符串造成的呢?
2.expandableLabelString为何为null?
3.如何复现问题?
4.如何修复?
一、第一个问题
1.首先看下Html.from()的源码:
public static Spanned fromHtml(String source) { return fromHtml(source, null, null); }
2.调用了其重写方法,源码如下:
public static Spanned fromHtml(String source, ImageGetter imageGetter, TagHandler tagHandler) { Parser parser = new Parser(); try { parser.setProperty(Parser.schemaProperty, HtmlParser.schema); } catch (org.xml.sax.SAXNotRecognizedException e) { // Should not happen. throw new RuntimeException(e); } catch (org.xml.sax.SAXNotSupportedException e) { // Should not happen. throw new RuntimeException(e); } HtmlToSpannedConverter converter = new HtmlToSpannedConverter(source, imageGetter, tagHandler, parser); return converter.convert(); }
其中,source(也就是expandableLabelString)通过HtmlToSpannedConverter类的构造方法传入HtmlToSpannedConverter的对象中,赋值给mSource对象句柄,并在converter.convert()方法中调用。
3.看下convert()方法
public Spanned convert() { mReader.setContentHandler(this); try { mReader.parse(new InputSource(new StringReader(mSource))); } catch (IOException e) { // We are reading from a string. There should not be IO problems. throw new RuntimeException(e); } catch (SAXException e) { // TagSoup doesn't throw parse exceptions. throw new RuntimeException(e); } ... }调用了StringReader的构造方法
public StringReader(String str) { this.str = str; this.count = str.length(); }当str为null时,必然会报出nullPointer的crash,而空字符串则不会。
二、看第二个问题:expandableLabelString为何为null
1.首先看下expandableLabelString的变量声明:
private String expandLabelString//展开的标签字段
2.再看下expandableLabelString的变量赋值处,只有一处:
expandLabelString = getExpandLabelString();其中,getExpandableString()方法
private String getExpandLabelString(){ String label = ""; // do label ... return label; }肯定会返回空字符串或者有值字符串,因此,expandableString在赋值过后不可能为null。
3.再看expandableLabelString的调用处,是在页面详情接口(网络请求,api)的回调中,那么expandableString为null,只有可能是接口数据未返回前,用户就进行了点击!这在网络情况不好或者服务端压力大时,是有可能出现的。
三、如何复现这个bug
为了验证自己的推论,在expandableString的赋值语句前加入断点,准备让网络请求结果处理中断,此时点击(执行onClick()),应该可以复现。
但是,执行到断点处时,发现app停留在上个页面,还没跳转到出现问题的页面(调用接口在onCreate()中,接口返回时间很短),这时想起来网络请求虽然是异步的,但结果处理是在UI线程里的,这就执行到UI线程里的断点了,相当于把UI线程阻塞了,此时页面既不能继续渲染,也无法执行任何点击操作。
因此,要将断点设置在网络请求的线程里,执行到断点,进行点击(调用onClick()),果然崩溃,堆栈信息与友盟报出的一致。
四、修复
原因定位清楚,问题修复就简单了,思路两个,一个是将expandableLabelString初始化为空字符串;另一个是,在网络接口数据返回前,不允许用户进行点击操作。前一个思路实现起来简单,后一个思路更加合理。
五、这个问题的总结:
1.对象句柄是否要初始化为默认值,有没有可能出现nullPointer的问题;
2.断点如果在UI线程里,UI线程就被阻塞了,此时既无法进行页面渲染,也无法响应用户点击;断点在非UI线程里,不会阻塞页面渲染的;
3.网络请求过程中,是否应该允许用户点击;
4.修复crash,一定要找清楚原因,不能头疼医头,脚疼医脚。
相关文章推荐
- dbj2 hash算法的实现
- 加载网络数据失败的原因:
- JSP/java 执行创建批处理文件,并执行批处理事务。
- MapReduce程式调用第三方包
- Parameter 'xxxx' not found. Available parameters are [list] at java.util.concurrent.FutureTask
- Java集合类操作优化经验总结
- Http协议与https协议区别
- [剑指offer]和为s的两个数 ; 和为s的连续正整数序列
- C++的三大特性:封装、继承、多态
- Express框架之connect-flash详解
- 【剑指offer系列】 调整数组顺序使奇数位于偶数前面___14
- eleasticsearch学习笔记(未完)
- UIWebView根据内容自适应高度
- 数据库语言面试题
- iOS 颜色插件失效的解决办法
- android怎么获取BitMap
- SQL Server 2005/2008 触发器的管理和查看
- 周记
- vim--配置(转)
- Android总结篇系列:Android 权限