【问题标题】:Error Cannot perform this operation because the connection pool has been closed错误无法执行此操作,因为连接池已关闭
【发布时间】:2026-02-21 21:40:01
【问题描述】:

我正在使用 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;
    }

我用谷歌搜索了好几个小时,但没有结果。请帮我修复它。任何帮助将不胜感激。谢谢!

【问题讨论】:

  • 你在哪里打开数据库?我的意思是 db.open();
  • @baboo 感谢您的回答,但我不使用内容提供程序,也从不调用 close db 方法。你能指出我的问题吗?
  • 关闭数据库前先关闭光标
  • 顺便问一下,DatabaseHandler.java 的第 533 行是什么?

标签: android sqlite


【解决方案1】:

打电话

db.open()

在这一行之后:

SQLiteDatabase db = this.getReadableDatabase();

但是,我个人不知道this.getReadableDatabase() 的具体情况如何,但我会制作自己的数据库适配器。有关详细信息,请参阅此链接:http://www.vogella.com/articles/AndroidSQLite/article.html

【讨论】:

【解决方案2】:

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

我认为异常是在“return StadiumStatus”行中引发的。上面几行,您将此变量分配给 Cursor 的成员。我相信您实际上是在指定名称 StadiumStatus 以指向仅存在于游标内的 int 变量。另一种可能性(这里是逻辑的巨大飞跃)是 Cursor.getInt() 返回的项目实际上是指向开放数据库中的项目(方法?)的指针(它驻留在内存中,因为它是开放的。)无论哪种方式,当您关闭游标和/或数据库,然后尝试返回 StadiumStatus 时,int 指向的地址 in 游标或 经过 游标,尝试跟随它,和封闭游标或封闭数据库的死胡同(其中之一是您从中绘制的“数据池”。)

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

"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 称 Cursors 为“持久连接”,暗示它不是从数据库返回值的简单集合。

【讨论】:

  • 我对此表示怀疑。 cur.getInt(11) 应该返回一个简单的 int,而不是一个对象。因此, StadiumStats 不是对 int 的引用,而只是保存 int 值。
【解决方案3】:

在您使用的代码中:

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

最好的做法是先关闭游标,再关闭数据库,因为一旦调用db.close(),就会导致数据库冻结n关闭,对应的游标无效。试试这个改变它会起作用。

替换为:

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

【讨论】:

    【解决方案4】:

    我发现在多线程中使用数据库时也会出现这个异常。所以最好在db = helper.getXXXDatabase()db.close()之间加上synchronized

    【讨论】:

      【解决方案5】:

      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();
          }
      

      【讨论】:

        【解决方案6】:

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

        【讨论】: