GithubHelp home page GithubHelp logo

SQLite异常整理 about androidbox HOT 2 OPEN

zengjingfang avatar zengjingfang commented on July 28, 2024
SQLite异常整理

from androidbox.

Comments (2)

zengjingfang avatar zengjingfang commented on July 28, 2024

异常详情

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)

资料查询

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.

获取信息

  • 设备故障导致
  • 文件打开时正在卸载文件系统导致

from androidbox.

zengjingfang avatar zengjingfang commented on July 28, 2024

异常详情

pro5 出现1次

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

public void endTransaction() {
    acquireReference();
    try {
        getThreadSession().endTransaction(null);
    } finally {
        releaseReference();
    }
}

public void acquireReference() {
    synchronized(this) {
        if (mReferenceCount <= 0) {
            throw new IllegalStateException(
                    // 抛出异常
                    "attempt to re-open an already-closed object: " + this);
        }
        mReferenceCount++;
    }
}

原因分析

我们和数据库只能有一条连接,即我们在调用dbHelper.getWritableDatabase()返回的是同一个对象,也就意味着我们在不同的线程需要用同一个dbHelper获取db。否则会报:android.database.sqlite.SQLiteDatabaseLockedException: database is locked (code 5) 。

有可能在别处调用了db.close方法,最终会调用releaseReference方法,导致mReferenceCount减一,当上述执行endTransaction时发现mReferenceCount<=0,就抛出了该异常。

解决方案

  • 不关法:用完不关闭(调用db.close方法),连接一直开着,不过存在性能的影响,观察源码可知。
    public void releaseReference() {
        boolean refCountIsZero = false;
        synchronized(this) {
            refCountIsZero = --mReferenceCount == 0;
        }
        if (refCountIsZero) {
            // 当应用层没有连接的时候调用这里,去释放数据库连接
            onAllReferencesReleased();
        }
    }
  • 计数法:

在外部添加一个获取连接的管理类,并进行计数,每打开一个加1,用完减1,如果没有了则调用关闭连接,调用db.close。
参考文章android sqlite数据库并发问题的详细描述和解决方案

知识扩展

AndroidSQLite的事务机制,保障了数据修改的准确性,避免出现脏数据。通过beginTransaction开启一个事务,然后执行自己的数据库操作语句,如果顺利执行完,就会执行setTransactionSuccessful,此时设置了一个标志位 mTransactionStack.mMarkedSuccessful = true。最后执行endTransaction,会进行前面标志位的判断,如果为true表明此次操作没有问题。就执行“COMMIT”提交数据,否则执行“ROLLBACK”,将数据回滚到本次事务操作之前的状态。

SQLiteSession#setTransactionSuccessful

    public void setTransactionSuccessful() {
        throwIfNoTransaction();
        throwIfTransactionMarkedSuccessful();
       // 事务顺利结束 在此设置成功的标志位
        mTransactionStack.mMarkedSuccessful = true;
    }

SQLiteSession#endTransactionUnchecked

private 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;
        }
    }

from androidbox.

Related Issues (20)

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.