错误无法执行此操作,因为已关闭连接池

时间:2013-02-20 09:47:09

标签: android sqlite

我正在使用sqlite db并使用Alex LockWood Correctly Managing Your SQLite Database

的一些代码

效果很好,但有时我收到错误"java.lang.IllegalStateException: Cannot perform this operation because the connection pool has been closed." 这是完整的错误:

02-20 16:37:21.385: W/dalvikvm(25730): threadid=13: thread exiting with uncaught exception (group=0x41c122a0)
02-20 16:37:21.390: E/AndroidRuntime(25730): FATAL EXCEPTION: Timer-0
02-20 16:37:21.390: E/AndroidRuntime(25730): java.lang.IllegalStateException: Cannot perform this operation because the connection pool has been closed.
02-20 16:37:21.390: E/AndroidRuntime(25730):    at android.database.sqlite.SQLiteConnectionPool.throwIfClosedLocked(SQLiteConnectionPool.java:963)
02-20 16:37:21.390: E/AndroidRuntime(25730):    at android.database.sqlite.SQLiteConnectionPool.waitForConnection(SQLiteConnectionPool.java:678)
02-20 16:37:21.390: E/AndroidRuntime(25730):    at android.database.sqlite.SQLiteConnectionPool.acquireConnection(SQLiteConnectionPool.java:349)
02-20 16:37:21.390: E/AndroidRuntime(25730):    at android.database.sqlite.SQLiteSession.acquireConnection(SQLiteSession.java:894)
02-20 16:37:21.390: E/AndroidRuntime(25730):    at android.database.sqlite.SQLiteSession.executeForCursorWindow(SQLiteSession.java:834)
02-20 16:37:21.390: E/AndroidRuntime(25730):    at android.database.sqlite.SQLiteQuery.fillWindow(SQLiteQuery.java:62)
02-20 16:37:21.390: E/AndroidRuntime(25730):    at android.database.sqlite.SQLiteCursor.fillWindow(SQLiteCursor.java:143)
02-20 16:37:21.390: E/AndroidRuntime(25730):    at android.database.sqlite.SQLiteCursor.getCount(SQLiteCursor.java:133)
02-20 16:37:21.390: E/AndroidRuntime(25730):    at com.uit.pokemon.DatabaseHandler.getStadiumStatusById(DatabaseHandler.java:533)
02-20 16:37:21.390: E/AndroidRuntime(25730):    at playground.RoomActivity.checkTable(RoomActivity.java:276)
02-20 16:37:21.390: E/AndroidRuntime(25730):    at playground.RoomActivity$6.run(RoomActivity.java:321)
02-20 16:37:21.390: E/AndroidRuntime(25730):    at java.util.Timer$TimerImpl.run(Timer.java:284)
02-20 16:37:21.460: I/timertask cancel(25730): canceled

以下是导致错误的代码:

public int getStadiumStatusById(int dataStadiumId){
        SQLiteDatabase db = this.getReadableDatabase();
        Cursor cur = db.rawQuery("SELECT " + keyStadiumId + " as _id, "
                + keyRoomName + ", " + keyP1Name + ", " + keyP1PokemonName
                + ", " + keyP1PokemonLevel + ", " + keyP1PokemonHp + ", "
                + keyP2Name + ", " + keyP2PokemonName + ", "
                + keyP2PokemonLevel + ", " + keyP2PokemonHp + ", "
                + keyTimeCreate + ", " + keyStadiumStatus + " from "
                + tbl_stadium + " WHERE " + keyStadiumId + " = " + "'"
                + dataStadiumId + "'", new String[] {});

        int stadiumStatus = 0;
        if(cur.getCount()>0)
        {
        cur.moveToFirst();
        stadiumStatus = cur.getInt(11);
        }
        db.close();
        cur.close();
        return stadiumStatus;
    }

我尝试谷歌搜索了很多个小时但没有结果。请帮我修理一下。任何帮助将不胜感激。谢谢!

6 个答案:

答案 0 :(得分:0)

我也在处理这个问题。我对原因有所了解,并希望听到专家对此的看法。我做了很多假设,得出了一些没有证据支持的结论......

   

我认为“返回stadiumStatus”行会抛出异常。上面几行,您将此变量分配给Cursor的成员。我相信你实际上是将sportsStatus这个名称指定为一个只存在于游标中的int变量。另一种可能性(逻辑的巨大飞跃)是Cursor.getInt()返回的项实际上是一个指向开放数据库中的项(一个方法?)的指针(由于它是打开的,它驻留在内存中)。无论哪种方式,当你关闭Cursor和/或数据库然后尝试返回stadiumStatus时,int指向 Cursor中的地址传递 Cursor,尝试跟随它,并且死角结束于关闭的Cursor或关闭的数据库(其中一个是您正在绘制的“数据池”。)

   

如果我是对的,我认为最好的解决办法是替换:

  

“stadiumStatus = cur.getInt(11);”

使用:

  

“stadiumStatus = new Integer(cur.getInt(11));”

从而在内存中创建一个新的Integer,vm可以简单地转换为int,并且仅依赖于其传统的可访问性范围。

   

这个答案有意义吗?我确信提问者早已解决了这个问题,但我希望能够说我回答了一个关于Stack Overflow的问题(或者告诉我为什么我错了,并且更好地理解游标和数据库)。

   

编辑:这些答案意味着我可能是对的:
      Android error: Cannot perform this operation because the connection pool has been closed
      Will cursor be still alive after database is closed?
   在第二个答案中,具有巨大声誉的用户DeeV将游标称为“持久连接”,这意味着它不是来自数据库的返回值的简单集合。

答案 1 :(得分:0)

在您使用的代码中:

db.close();
cur.close();

最佳做法是首先关闭游标然后关闭数据库,因为一旦调用db.close(),它将使数据库冻结关闭并且其相应的游标无效。尝试这种改变它会起作用。

将其替换为:

cur.close();
db.close();

答案 2 :(得分:0)

我发现在多线程中使用数据库时也会发生此异常。因此,最好在synchronizeddb = helper.getXXXDatabase()

之间添加db.close()

答案 3 :(得分:0)

SQLiteOpenHelper类打开的数据库在类的生命周期内保持打开状态。

因此无需打开和关闭数据库。事实上,如果它在多个线程上使用,那就错了。

我的应用程序已经有一段时间没有数据库错误了,我有一个帮助类。这需要是一个单例,因此SqliteDatabase的不同实例不会同时尝试修改该文件。

此外我曾经发现的一些崩溃表明游标和查询都可以调用sqlite查询,因​​此允许对数据库进行并发读写访问是没有意义的。

出于这个原因,我向助手添加了一个读写器锁,并使用这种模式...

    public static class MyDbHelper extends SQLiteOpenHelper {
        private MyDbHelper (Context context) {
            super(context, DATABASE_NAME, null, DATABASE_VERSION);
        }
        public static MyDbHelper mInstance;
        private static Object mObj = new Object();
        public static MyDbHelper getInstance( Context context ){
            synchronized (mObj ){
                if( mInstance == null ){
                    mInstance = new MyDbHelper ( context );
                }
            }
            return mInstance;
        }
        private ReentrantReadWriteLock mLock = new ReentrantReadWriteLock();
        public Lock readLock() {
            return mLock.readLock();
        }
        public Lock writeLock() {
            return mLock.writeLock();
        }

使用类是这样的......

    MyStorageContract.MyDbHelper helper = MyStorageContract.MyDbHelper.getInstance(this);
    SQLiteDatabase db = helper.getReadableDatabase();
    helper.readLock().lock();
    try {
        String[] columns = {
                MyStorageContract.Libraries.COLUMN_NAME_LIBRARY_ID,
                MyStorageContract.Libraries.COLUMN_NAME_LIBRARY_DIR,
                MyStorageContract.Libraries.COLUMN_NAME_LIBRARY_SOMETHING,
        };
        String result = null;
        Cursor c = db.query(MyStorageContract.Libraries.TABLE_NAME, columns, null, null, null, null, null, "1");
        int idxDir = c.getColumnIndex(MyStorageContract.Libraries.COLUMN_NAME_LIBRARY_DIR);
        int idxRoot = c.getColumnIndex(MyStorageContract.Libraries.COLUMN_NAME_LIBRARY_SOMETHING);
        int idxId = c.getColumnIndex(MyStorageContract.Libraries.COLUMN_NAME_LIBRARY_ID);
        for (c.moveToFirst(); !c.isAfterLast(); c.moveToNext()) {
            result = c.getString(idxDir);
        }
        c.close();
        return result;
    }
    finally {
        helper.readLock().unlock();
    }

答案 4 :(得分:0)

我遇到了这个问题并在此解决了java.lang.IllegalStateException: Cannot perform this operation because the connection pool has been closed。在我的例子中,我分析了android的数据库框架。 mime的真正原因是我在许多用于保存和查询数据的商店对象中使用SQLiteDatabase。由于SQLiteOpenHelper是单例,因此SQLiteDatabase也是单例。即使我在一个商店中添加了sychronized,它仍然在另一个商店中解锁。因此,如果我忘记在一个地方删除SQLiteDatabase.close(),它将导致SQLiteConnectionPool关闭。此外,我还添加了一些方法来释放SQLite连接。希望它有所帮助。

答案 5 :(得分:-6)

致电

db.open()

这一行之后:

SQLiteDatabase db = this.getReadableDatabase();

但是,我个人不知道this.getReadableDatabase()究竟是怎么做的,但我只想制作自己的数据库适配器。有关详细信息,请参阅此链接:http://www.vogella.com/articles/AndroidSQLite/article.html