由一个Buffer引发的血案

背景

现在的项目需要是:服务器端使用Servlet+JDBC技术从Oracle服务器读取内容写入SQLite数据库文件,Android客户端下载这个文件并在目标Android系统上使用它。

问题再现

上述需求似乎并不复杂,但在我下载了生成的数据库文件之后,却连基本的SQL语句都执行不了。例如有一个表名为t_name,当我执行下面的代码时,却出现异常(db是从该文件打开的SQLiteDatabase对象):

Cursor cursor = db.rawQuery(“select * from t_name”, null);

部分异常堆栈信息如下:

关键代码

生成服务器文件的代码和Android客户端读取文件的代码都很简单,这里只贴出下载程序的部分代码:

//source是目标文件的URLURLConnection conn = source.openConnection();BufferedInputStream bis = new BufferedInputStream(conn.getInputStream());//target 是目标文件名FileOutputStream fos = new FileOutputStream(target);byte[] buf = new byte[1024];int read=0;while ((read=bis.read(buf))>0){fos.write(buf);}bis.close();fos.close();

解决过程

1.首先认为只是偶然现象,卸载项目,重新安装,毫无变化。

2.检查数据库的打开和关闭操作,因为之前也遇到过镜像文件破坏问题,就是由于文件打开之后没有关闭。代码检查完毕,没有这种泄漏问题。

3.怀疑是不是生成的数据库文件有问题。在服务器上新建一个Java SE项目,使用标准JDBC对该文件进行读取,一切正常。

4.认为可能在下载数据库文件时发生了传输错误。清除数据,重新下载,错误信息仍然存在。

5.借助可视化管理工具SQLiteStudio打开服务器文件,查看文件内容,完全正常;使用adb将客户端下载好的文件传回电脑并打开,无法正常查询,收到如下警告。

7.问题根源找到,既然缓冲区大了会多写空白符,那么很明显在某次(理论上应该总是最后一次)如果没有把缓冲区读满,上面的代码也会写入空白符到db文件中。数据库镜像错误就不难理解了。

8.修改后的代码如下,修改后问题解决。

//source是目标文件的URLURLConnection conn = source.openConnection();BufferedInputStream bis = new BufferedInputStream(conn.getInputStream());//target 是目标文件名FileOutputStream fos = new FileOutputStream(target);byte[] buf = new byte[1024];int read=0;while ((read=bis.read(buf))>0){//这行代码是关键代码fos.write(buf, 0, read);}bis.close();fos.close();

心得总结

这次的Bug困扰了我两天,最后居然是在近乎意外的情况下找到的问题,深受教训,总结起来大约有以下几点:

本文出自 “飞翔的猫咪” 博客,请务必保留此出处

陪我们走过一段别人无法替代的记忆。

由一个Buffer引发的血案

相关文章:

你感兴趣的文章:

标签云: