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

时间:2014-04-25 12:44:52

标签: java android database sqlite singleton

我正在阅读有关此问题的stackoverflow,但我仍然没有找到解决方案。我注意到有时,我的应用程序会抛出此错误:

   java.lang.IllegalStateException: Cannot perform this operation because the connection pool has been closed.
        at android.database.sqlite.SQLiteConnectionPool.throwIfClosedLocked(SQLiteConnectionPool.java:962)
        at android.database.sqlite.SQLiteConnectionPool.waitForConnection(SQLiteConnectionPool.java:599)
        at android.database.sqlite.SQLiteConnectionPool.acquireConnection(SQLiteConnectionPool.java:348)
        at android.database.sqlite.SQLiteSession.acquireConnection(SQLiteSession.java:894)
        ...

我有一个名为DatabaseHelper.java的文件,使用这种方法获取它的实例:

public static DatabaseHelper getInstance(Context context) {
    if (mInstance == null) {
        mInstance = new DatabaseHelper(context.getApplicationContext());
    }
    return mInstance;
}

然后我有类似这样的方法(它在行cursor.moveToFirst()中崩溃并出现该错误)。它几乎不会崩溃,但有时会崩溃。

public Profile getProfile(long id) {
    SQLiteDatabase db = this.getReadableDatabase();

    String selectQuery = "SELECT * FROM " + TABLE_PROFILES + " WHERE " + KEY_PROFILES_ID + " = " + id;
    Cursor cursor = db.rawQuery(selectQuery, null);

    // looping through all rows and adding to list
    Profile profile = new Profile();
    if (cursor.moveToFirst()) {
        doWhatEver();
    }
    cursor.close();
    db.close();

    return profile;
}

就是这样,在我使用的所有方法中:

SQLiteDatabase db = this.getReadableDatabase(); (or Writable)

然后我关闭光标和数据库。在这种情况下,错误就行了:

cursor.moveToFirst();

如果我之前调用this.getReadableDatabase(),我不明白为什么错误说数据库已关闭。请支持!谢谢:))

8 个答案:

答案 0 :(得分:74)

删除

db.close();

如果在关闭数据库后尝试其他操作,它将为您提供该异常。

documentation说:

  

释放对象的引用,关闭对象......

另外,退房 Android Sq Lite closed exception关于来自Android Framework工程师的评论,该评论声明关闭数据库连接不是必需的。

答案 1 :(得分:17)

我目前遇到同样的问题。虽然删除db.close()为我解决了问题,但我认为这个问题是由多线程引起的。这是我的研究。

SQLiteOpenHelper保存对SQLiteDatabase的引用,当调用getReadableDatabase()或getWritableDatabase()时,它将返回引用,如果SQLiteDatabase 关闭或为null,则将创建新的SQLiteDatabase对象。请注意,在get方法中,代码在同步块中受到保护。

SQLiteDatabase是SQLiteClosable的子类。 SQLiteClosable实现了引用计数方案。

  • 首次创建时,计数为1.

  • 当数据库操作方法运行时(如insert,query),它会增加计数,并在方法结束时减少计数。但是游标操作不受引用计数的保护。

  • 如果计数减少到0,则连接池将关闭,成员SQLiteConnectionPool对象将设置为null,现在SQLiteDatabase 已关闭;

  • SQLiteDatabase.close()会将计数减少1;

因此,如果你有一个单线程方案,关闭SQLiteDatabase就可以了,因为SQLiteOpenHelper只会重新创建它。

如果你做多线程,那么你会遇到麻烦。假设线程A和线程B都调用getReadableDatabase(),并且SQLiteOpenHelper返回它保存的SQLiteDatabase,然后线程A首先完成其操作并调用SQLiteDatabase.close(),现在SQLiteDatabase对象线程B已经关闭所以任何后续的db操作调用或游标方法调用都会抛出异常。

答案 2 :(得分:3)

我遇到了同样的问题,无法修复它。我找到了一条可能的线索: 我有一个一直在运行的同步线程:

    Item ii = dbHelper.popPendingUpload();
    if (ii != null)
            upload(ii);

在DBHelper里面

public Item popPendingUpload() {

    SQLiteDatabase db = getReadableDatabase();
    Cursor res = db
            .rawQuery("SELECT * FROM items WHERE state = 0 LIMIT 1",
                    new String[] {});
    boolean hasNext = res.moveToFirst();
    Item ii = null;
    if (hasNext) {
        ii = //load item
    }
    db.close();
    return ii;
}

错误也会出现在moveToFirst()方法调用中。当线程在无限循环中弹出项目时,第一次它正常工作,第二次出现错误。有趣的是,如果我放置一个断点并逐步执行代码,则错误不再显示。我正在使用Android 4.1在真实设备上进行测试。

我知道,这不是一个答案,但它可能有所帮助。我会继续测试。

答案 3 :(得分:3)

//close database after cursor is closed like:

if(cursor.getCount() != 0){

    while(cursor.moveToNext()){
      //do operation
    }
}

cursor.close();
database.close();

答案 4 :(得分:0)

也许您在从应用访问数据库之前关闭了数据库。

您必须将getProfile()编辑为

class FooClass(object):
    def __init__(self, *args, **kwargs):
        self.foos = ["foo", "bar"]

    def do_foos(self):
        for foo in self.foos:
            print("I have a " + foo)

class SpecialisedFooClass(FooClass):
    def __init__(self, *args, **kwargs):
        super(SpecialisedFooClass, self).__init__(*args, **kwargs)
        self.foos.append("spec")

答案 5 :(得分:0)

我有一个像你这样的错误,这里是我的代码:

  try {
                        String sql = "SELECT * FROM "+DB_TABLEDOWNLOAD;
                        Cursor cursor = db.rawQuery(sql, null);
                        //空双引号为原表没有的字段
                        String temp = "";
                        int existIconPath = cursor.getColumnIndex("iconPath");
                        int existAudioID = cursor.getColumnIndex("audioID");

                        if (existIconPath == -1 && existAudioID == -1){
                            temp = "url, downed,total,path,name, audioTitle, audioFileID,\"\",\"\"";
                        }else if (existIconPath == -1 && existAudioID != -1){//iconPath不存在
                            temp = "url, downed,total,path,name, audioTitle, audioFileID,\"\",audioID";
                        }else if (existIconPath != -1 && existAudioID == -1){//audioID不存在
                            temp = "url, downed,total,path,name, audioTitle, audioFileID,iconPath,\"\"";
                        }else {
                            return;
                        }

                        db.beginTransaction();
                        String tempTableName = "_temp_"+DB_TABLEDOWNLOAD;
                        String sqlCreateTemp = " ALTER TABLE "+DB_TABLEDOWNLOAD+" RENAME TO "+tempTableName+";";
                        db.execSQL(sqlCreateTemp);

                        final String TB_TESTPAPERINFO_CREATE = "Create  TABLE IF NOT EXISTS "
                                + DB_TABLEDOWNLOAD
                                + "(url TEXT, downed TEXT,total TEXT,path TEXT,name TEXT, audioTitle TEXT, audioFileID TEXT,iconPath TEXT, audioID TEXT);";
                        db.execSQL(TB_TESTPAPERINFO_CREATE);

                        String sqlBackupData = "INSERT INTO "+DB_TABLEDOWNLOAD+" SELECT "+temp+" FROM "+tempTableName+";";
                        db.execSQL(sqlBackupData);
                        String sqlDrop = "DROP TABLE IF EXISTS '"+tempTableName+"';";
                        db.execSQL(sqlDrop);
                        db.setTransactionSuccessful();
                    } catch (Exception e) {
                        e.printStackTrace();
                    }finally{
                        db.endTransaction();
                    }

我在db.beginTransaction()之前使用return,我的代码在beginTransaction之前返回,但是我终于在endTransaction中。如果你没有beginTransaction和endTransaction,则会出现异常。

请检查有关db.beginTransaction和endTransaction的代码。

答案 6 :(得分:0)

这是使用getApplicationContext()初始化房间数据库的Android体系结构中的一个简单错误,这意味着您的应用程序具有数据库引用的一个实例,无论有多少活动创建了该实例的实例,因此如果在其中关闭它任何其他活动都会引发该异常。

本质上,不只是检查引发异常的活动,而是db.close()的每个活动

答案 7 :(得分:0)

如果有人在搜索这篇文章时遇到同样的情况,请提一下出现此错误的另一种可能性:

在 Android Studio 中使用 Database Inspector 在某些情况下可能会导致此错误。