中秋节前项目报出一个Bug,改了多次都没有解决好,后来发现问题出在一条SQLite删除语句上,这条语句完全没有像我预想的方式运行,而细细一看,确又是自己粗心大意自以为是的理解才会这样写代码的。
看一下这个样例代码:
有发现问题吗?
public int delete(String table, String whereClause, String[] whereArgs)
我把whereClause字符串中的"?"理解成会被whereArgs中的第1个String(whereArgs[0])替换,而实际中并不是直接替换,而是进行和"?"的映射小绑定操作,最终交给这两个值仍然是分别传给SQLite的Native层去操作的(可以查看SQLiteProgram.java类的bindAllArgsAsStrings)。
也就是说,当只有一个值时,如上图第一句代码中"?"被绑定成了"18",操作结果是正确的。但第3条语句,"?"被绑定成"18,22"进,因为不是直接替换,所以在Native层操作时,NOT IN会去匹配你要的内容在不在"18,22"中,而不是去匹配在不在"18"和"22"中,它把"18,22"现解成一个新的字符串,而不是我想的用","分成两个值。
没有不能重现的Bug
再聊一点,这个问题为什么我改了多次才找到问题的根源在这里呢?因为我们调用这个语句的地方与多进程(android:process)有关,而我本机测试时这些进程不会启动,使得NOT IN (?) 中“?”对应的是一个值,所以delete语句的执行结果是对的,而当我把其他进程也启动起来时,这个问题基本上百分百重现。
在面对测试提交的Bug时,常会遇到一些重现概率较低的Bug,这类Bug也非常让开发头疼,因为如果日志正好没有记录下来又不能重现的话,很难解决问题,而且就算你解决了,你怎么证明你把这个问题解决了呢?你无法验证。
下在列举几点对于低重现率Bug可以留意的地方:
是否有多线程在操作
很多问题都和这个有关,有些手机重现了,而同样的包在另一些手机上又正常,而当你直接Debug调试时发现完全没有问题。你要知道Debug会让程序运行变慢,而断点处会让当前线程中断(你的断点可能设置在主线程),这种时候你可以往多线程同步等方面想想,这个Bug是多线程问题的概率很大。所以查查是不是有多线程使用共享数据,有时某个线程跑快了就发生问题,而下次这个线程跑慢了就是正常的。内存问题
内存方面的问题是测试无法说明的,他们一般不能判断是内存问题。具体给你的Bug描述里,他们会写这个Bug只发生过一次!那么他们提交的Bug重现步骤基本上也没有意义,因为他们自己按这个步骤来都重现不出来。这样的问题,你可以考虑一下是否和内存有关(内存问题是可以重日志看出一点蛛丝马迹),因为系统在内存紧张时可能会杀掉进程或者销毁Activity并释放它点用的资源,而你的代码没有注意这些边界处理的话,是很有可能发生问题的。
有时候,我们通过DDMS中把应用的进程直接KILL掉就发现问题百分百重现了。而测试在测试时可能打开了很多应用,使用了很久才产生这个条件,而当问题发生时,系统释放了一部份内存,这个问题产生的前提条件就没了。重现步骤是否会涉及一些Android特性
这种问题也很常见,测试在操作Bug产生的步骤中可能触发了一个系统事件(或者被系统事件中断),但他们可能忘记记录进Bug描述里(测度可能觉得这个事件无关紧要)。如正在测试时,电话或者短信来了,再或者在某个界面手一抖转了一下屏,都可能发生问题。所以当我们无法重现Bug时,不妨在某个步骤里增加一些转屏、销屏或者点击通知栏等操作,也许问题马上就能百分百重现了。