用SQLite做记录数据库用时,出现了如下异常
E/CrashRecorder: android.database.sqlite.SQLiteCantOpenDatabaseException:
unable to open database file (code 14)
at android.database.sqlite.SQLiteConnection.nativeExecuteForCursorWindow(Native Method)
at android.database.sqlite.SQLiteConnection.executeForCursorWindow(SQLiteConnection.java:843)
at android.database.sqlite.SQLiteSession.executeForCursorWindow(SQLiteSession.java:836)
at android.database.sqlite.SQLiteQuery.fillWindow(SQLiteQuery.java:62)
android.database.sqlite.SQLiteCursor.fillWindow(SQLiteCursor.java:145)
android.database.sqlite.SQLiteCursor.getCount(SQLiteCursor.java:134)
先回顾下异常场景,常驻线程每10秒定时查询30个记录表的元数据数量,开始一切正常,到了第4分钟,程序在小米4(安卓6.0),在三星手机(安卓5.0)和联想手机(安卓4.3)上异常退出。
第一是猜想是不是数据库文件被清掉,进adb查看下,文件还在数据也没有问题。
这时度娘了下,找到了这边博文
再次非常感谢@梦里风林 博主的分享
https://ahangchen.gitbooks.io/windy-afternoon/content/android/sqlite/SQLITE在ANDROID上的一个BUG.html
结论
这是sqlite在Android系统上的一个bug,在需要建立索引的sql语句频繁执行时,会发生这个异常。
(如果你是在SQLiteDatabase执行open()时看到的这个exception,那应该是线程冲突的问题,跟这篇文章讲的不是同一个)
根本原因是sqlite临时文件目录不可用。 解决方案是第一次建立连接时设置临时文件目录。
在项目里遇到了这样一个奇怪的crash,长期占据各个版本crash上报榜首,但在开发中一直不能重现。
在许多查DB的代码路径里,都会在moveToFirst(),getCount()等需要执行fillWindow的地方出现这个crash。
按照他的思路,先把这些操作全部try – catch住
然后修改代码
PRAGMA temp_store_directory = 'your dir'
//这个东西仅对当前SqliteConncetion有效,
//在第一次建立sqlite连接的时候(我是重写了getReadabelDatabase()方法),设置一下临时文件目录,like this:
//private static boolean mainTmpDirSet = false;
@Override
public SQLiteDatabase getReadableDatabase() {
if (!mainTmpDirSet) {
boolean rs = new File("/data/data/com.cmp.pkg/databases/main").mkdir();
Log.d("ahang", rs + "");
super.getReadableDatabase().execSQL("PRAGMA temp_store_directory = '/data/data/com.cmp.pkg/databases/main'");
mainTmpDirSet = true;
return super.getReadableDatabase();
}
return super.getReadableDatabase();
}
此时再进行测试,到了第12分钟,程序在小米4(安卓6.0)复现,在三星手机(安卓5.0)和联想手机(安卓4.3)一切正常。
此时,基本上可以定位到这个异常原因了,果然是频繁执行cout(id)操作导致的,此时我采用了在插入记录到数据库时机,和删除记录到数据库时机时用Map维护了一个cout记录,实际上就减少了对数据库的访问,
最后验证成功