net.sqlcipher.database.SQLiteException: error code 5: database is locked
at net.sqlcipher.database.SQLiteStatement.native_execute(Native Method)
at net.sqlcipher.database.SQLiteStatement.executeInsert(SQLiteStatement.java:84)
at com.j256.ormlite.sqlcipher.android.AndroidDatabaseConnection.insert(AndroidDatabaseConnection.java:158)
at com.j256.ormlite.stmt.mapped.MappedCreate.insert(MappedCreate.java:91)
at com.j256.ormlite.stmt.StatementExecutor.create(StatementExecutor.java:450)
at com.j256.ormlite.dao.BaseDaoImpl.create(BaseDaoImpl.java:310)
at com.xtc.database.encrypt.OrmLiteDao.insert(OrmLiteDao.java:99)
at com.xtc.bigdata.report.db.ReportDao.insert(ReportDao.java:46)
at com.xtc.bigdata.report.db.UserBehaviorProvider.insert(UserBehaviorProvider.java:113)
at android.content.ContentProvider$Transport.insert(ContentProvider.java:264)
at android.content.ContentProviderNative.onTransact(ContentProviderNative.java:163)
at android.os.Binder.execTransact(Binder.java:565)
如上异常堆栈中的错误信息error code 5: database is locked
,经过查找发现code为5代表sqlite中的SQLITE_BUSY异常,详见:https://www.sqlite.org/rescode.html#busy,这里面说,SQLITE_BUSY(5)异常是一个数据库文件在被其他不同的数据库连接进行并发操作的时候写操作将补发继续,通常是多个进程的不同数据库连接对同一个数据库进行并发操作,例如进程A在进行耗时的数据库事务,而于此同时进程B也要进行一个数据库事务,这时候进程B就会直接返回SQLITE_BUSY的错误码,因为sqlite只能支持同一个时刻只能有一个写操作,所以解决这个问题的方法就是避免不同进程分别对同一个数据库各自开启一个database connection,并且对相同的数据库进行并发操作,如果有这种需求,那么应该全部都交给一个进程来对数据库进行操作,其他的进程想操作这个数据库就通过contentprovider的方式来实现数据共享,使用contentprovider的方式是最安全的,如果是通过shareUserId的方式来实现数据库共享也是不安全的,因为:
Context thdContext = null;
try {
thdContext = createPackageContext(
"com.example.testdatabase",
Context.CONTEXT_IGNORE_SECURITY);
String dbPath = thdContext.getDatabasePath("BookStore.db")
.getAbsolutePath();
SQLiteDatabase db = SQLiteDatabase.openDatabase(dbPath,
null, SQLiteDatabase.OPEN_READWRITE);
Cursor cursor = db.query("Book", null, null, null, null,
null, null);
if (cursor.moveToFirst()) {
do {
String name = cursor.getString(cursor
.getColumnIndex("name"));
Log.d(TAG, "name: " + name);
} while (cursor.moveToNext());
}
} catch (NameNotFoundException e) {
e.printStackTrace();
}
SQLiteDatabase.openDatabase会创建一个数据库实例SQLiteDatabase,如果在不同的进程如果通过shareuserid来实现数据库共享,那么会造成每一个进程都有SQLiteDatabase对象,在并发操作的时候也有可能会出现如上问题,所以还是推荐使用contentprovider的方式来实现数据库共享,必须注意contentprovider必须只有宿主app进程来维护,其他的进程就通过调用宿主app进程的contentprovider暴露出去的接口来实现对宿主app进程的数据库的操作,实际上这时候的数据库操作就都是由宿主app进程来操作的了,就不会出现如上的异常
We are glad to hear 3.5.7 is working well for you. We've adjusted the library to allow it to dynamically resize the backing memory for the cursor, so you would be limited by the device.
详见:https://github.com/sqlcipher/android-database-sqlcipher/issues/341#issuecomment-310289295,现在是改成动态来分配大小的,所以限制的上限就会由机器来决定,也就是说,仍然存在这个问题,如果存入数据库的记录太大,还是有可能发生此异常,我们不建议让sqlite数据库中去存储blog这种大的数据记录,应该大的数据记录存成文件,然后把文件路径存到数据库中会更加合适