-
Notifications
You must be signed in to change notification settings - Fork 3
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
SQLite异常整理 #27
Comments
异常详情android.database.sqlite.SQLiteDiskIOException: disk I/O error - SQLITE_IOERR_WRITE (Sqlite code 778), (OS error - 9:Bad file descriptor) 资料查询https://www.sqlite.org/rescode.html 官方解释The SQLITE_IOERR_WRITE error code is an extended error code for SQLITE_IOERR indicating an I/O error in the VFS layer while trying to write into a file on disk. This error might result from a hardware malfunction or because a filesystem came unmounted while the file was open. This error should not occur if the filesystem is full as there is a separate error code (SQLITE_FULL) for that purpose.
|
异常详情
java.lang.IllegalStateException: attempt to re-open an already-closed object: SQLiteDatabase: /data/user/0/com.xtc.watch/databases/xtc_im.db
at android.database.sqlite.SQLiteClosable.acquireReference(SQLiteClosable.java:55)
at android.database.sqlite.SQLiteDatabase.endTransaction(SQLiteDatabase.java:522)
at com.xtc.im.core.push.store.dao.ServerConfigDao.update(Unknown Source)
at com.xtc.im.core.push.domain.DomainManager.updateOnConnectFailed(Unknown Source)
at com.xtc.im.core.push.PushService$1$4.run(Unknown Source)
at android.os.Handler.handleCallback(Handler.java:751)
at android.os.Handler.dispatchMessage(Handler.java:95)
at android.os.Looper.loop(Looper.java:154)
at android.os.HandlerThread.run(HandlerThread.java:61) 应用代码public void update(DBServerConfigEntity entity) {
SQLiteDatabase db = dbHelper.getWritableDatabase();
// 事务开始
db.beginTransaction();
Cursor cursor = null;
try {
cursor = db.query(DBServerConfigEntity.TABLE, null, DBServerConfigEntity.KEY_ID + "=?", new String[]{String.valueOf(entity.getId())}, null, null, null);
DBServerConfigEntity oldEntity = null;
if (cursor == null) {
LogUtil.e(TAG,"cursor is null ,the config we update is not exist !!!");
return;
}
if (cursor.moveToFirst()) {
oldEntity= serverConvertToEntity(cursor);
}
if (oldEntity == null) {
LogUtil.e(TAG,"old server config is null ,the config we update is not exist !!!");
return;
}
ContentValues values = serverConvertToValues(entity, oldEntity);
db.update(DBServerConfigEntity.TABLE, values, DBServerConfigEntity.KEY_ID + "=?", new String[]{String.valueOf(entity.getId())});
// 事务 成功
db.setTransactionSuccessful();
} catch (Exception e) {
LogUtil.e(TAG, e);
} finally {
// 事务结束
db.endTransaction();
if (cursor != null) {
cursor.close();
}
}
} 源码追踪SQLiteDatabase.java
原因分析我们和数据库只能有一条连接,即我们在调用dbHelper.getWritableDatabase()返回的是同一个对象,也就意味着我们在不同的线程需要用同一个dbHelper获取db。否则会报:android.database.sqlite.SQLiteDatabaseLockedException: database is locked (code 5) 。 有可能在别处调用了db.close方法,最终会调用releaseReference方法,导致mReferenceCount减一,当上述执行endTransaction时发现mReferenceCount<=0,就抛出了该异常。 解决方案
public void releaseReference() {
boolean refCountIsZero = false;
synchronized(this) {
refCountIsZero = --mReferenceCount == 0;
}
if (refCountIsZero) {
// 当应用层没有连接的时候调用这里,去释放数据库连接
onAllReferencesReleased();
}
}
在外部添加一个获取连接的管理类,并进行计数,每打开一个加1,用完减1,如果没有了则调用关闭连接,调用db.close。 知识扩展AndroidSQLite的事务机制,保障了数据修改的准确性,避免出现脏数据。通过beginTransaction开启一个事务,然后执行自己的数据库操作语句,如果顺利执行完,就会执行setTransactionSuccessful,此时设置了一个标志位 mTransactionStack.mMarkedSuccessful = true。最后执行endTransaction,会进行前面标志位的判断,如果为true表明此次操作没有问题。就执行“COMMIT”提交数据,否则执行“ROLLBACK”,将数据回滚到本次事务操作之前的状态。 SQLiteSession#setTransactionSuccessful public void setTransactionSuccessful() {
throwIfNoTransaction();
throwIfTransactionMarkedSuccessful();
// 事务顺利结束 在此设置成功的标志位
mTransactionStack.mMarkedSuccessful = true;
} SQLiteSession#endTransactionUncheckedprivate void endTransactionUnchecked(CancellationSignal cancellationSignal, boolean yielding) {
if (cancellationSignal != null) {
cancellationSignal.throwIfCanceled();
}
final Transaction top = mTransactionStack;
boolean successful = (top.mMarkedSuccessful || yielding) && !top.mChildFailed;
RuntimeException listenerException = null;
final SQLiteTransactionListener listener = top.mListener;
if (listener != null) {
try {
if (successful) {
listener.onCommit(); // might throw
} else {
listener.onRollback(); // might throw
}
} catch (RuntimeException ex) {
listenerException = ex;
successful = false;
}
}
mTransactionStack = top.mParent;
recycleTransaction(top);
if (mTransactionStack != null) {
if (!successful) {
mTransactionStack.mChildFailed = true;
}
} else {
try {
if (successful) {
// 事务成功 则提交
mConnection.execute("COMMIT;", null, cancellationSignal); // might throw
} else {
// 事务失败 则回滚
mConnection.execute("ROLLBACK;", null, cancellationSignal); // might throw
}
} finally {
releaseConnection(); // might throw
}
}
if (listenerException != null) {
throw listenerException;
}
} |
android.database.sqlite.SQLiteDiskIOException: disk I/O error - SQLITE_IOERR_WRITE (Sqlite code 778), (OS error - 9:Bad file descriptor)
at android.database.sqlite.SQLiteConnection.nativeExecute(Native Method)
at android.database.sqlite.SQLiteConnection.execute(SQLiteConnection.java:563)
at android.database.sqlite.SQLiteSession.endTransactionUnchecked(SQLiteSession.java:437)
at android.database.sqlite.SQLiteSession.endTransaction(SQLiteSession.java:401)
at android.database.sqlite.SQLiteDatabase.endTransaction(SQLiteDatabase.java:541)
at com.xtc.im.core.push.store.dao.ServerConfigDao.update(Unknown Source)
at com.xtc.im.core.push.domain.DomainManager.updateOnConnectSuccess(Unknown Source)
at com.xtc.im.core.push.PushService$1$1.run(Unknown Source)
at android.os.Handler.handleCallback(Handler.java:743)
at android.os.Handler.dispatchMessage(Handler.java:95)
at android.os.Looper.loop(Looper.java:150)
at android.os.HandlerThread.run(HandlerThread.java:61)
The text was updated successfully, but these errors were encountered: