我的代码中有一个异常。
10-10 22:05:33.866 5725 5725 E AndroidRuntime: Caused by: net.sqlcipher.database.SQLiteDiskIOException: error code 10: disk I/O error
10-10 22:05:33.866 5725 5725 E AndroidRuntime: at net.sqlcipher.database.SQLiteStatement.native_execute(Native Method)
10-10 22:05:33.866 5725 5725 E AndroidRuntime: at net.sqlcipher.database.SQLiteStatement.execute(SQLiteStatement.java:58)
10-10 22:05:33.866 5725 5725 E AndroidRuntime: at net.sqlcipher.database.SQLiteDatabase.insertWithOnConflict(SQLiteDatabase.java:2074)
这在数千个成功的插入和选择中只发生过一次(到目前为止),所以我怀疑同一进程中的不同线程之间存在某种竞争条件,试图访问同一个数据库。
实际代码并不重要,这只是一个简单的新记录插入,on conflict 参数表示忽略任何冲突:
ContentValues values = new ContentValues();
// values.put(); calls go here, nothing strange about the data
db.insertWithOnConflict("Sample", null, values, SQLiteDatabase.CONFLICT_IGNORE);
从架构上讲,我的服务中有一个单例对象,它封装了一个与数据库的连续打开的连接,前面有一个缓存。多个线程从服务中的不同位置调用对象以插入数据,这些通常是侦听器,它们正在记录诸如 GPS 位置、电池状态等内容。读取通常来自缓存并且不会命中数据库。
我的第一个想法是,也许我需要同步数据库访问流所通过的类(闻起来像是两个不同的线程可能几乎同时尝试插入,实际上这种情况很少发生),但这个答案似乎是说我应该可以毫无问题地做到这一点。“从多个线程中插入、更新、删除和读取通常都可以”值得注意的是,异常表示“I/O 错误”而不是“数据库锁定”。
更新:我在每个数据库访问中添加了 logcat 跟踪和异常捕获。在那之后,我又遇到了两个例外。在这两种情况下,只有一个数据库访问正在进行。两者都是简单的插入。第一个在插入开始后 1.2 秒抛出异常,这看起来高得令人难以置信。第二个只用了 38 毫秒,这看起来是平均水平。
问题不在于存储空间不足:我有 27GB 可用空间。我也很难相信这也是闪存的问题,因为我没有看到写入它的任何其他错误。对于使用相同 SQLCipher 库但在每次插入后关闭数据库的旧应用程序,我也没有看到任何类似的问题。
更新 2:在异常处理程序中,我添加了代码以立即重试插入,到目前为止,我还没有收到第二个异常:
try {
ret = db.insertWithOnConflict("Sample", null, values, SQLiteDatabase.CONFLICT_IGNORE);
} catch (Exception e) {
Log.d(logtag,"exception:"+e);
try {
ret = db.insertWithOnConflict("Sample", null, values, SQLiteDatabase.CONFLICT_IGNORE);
} catch (Exception e1) {
Log.d(logtag,"exception2:"+e1);
}
return 0;
}