Android: не удается выполнить эту операцию, поскольку пул соединений закрыт

42

Я читал через 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)

И затем я закрываю курсор и db. В этом случае ошибка попала в строку:

cursor.moveToFirst();

Я не понимаю, почему ошибка говорит о том, что db закрывается, если я вызываю this.getReadableDatabase() раньше. Пожалуйста Поддержи! Спасибо:)

  • 0
    Привет @Ferran, Можете ли вы предложить, какой ответ является принятым ответом и помог вам решить ваш вопрос? Это поможет другим.
Теги:
database
singleton

6 ответов

58

Удалить

db.close();

Если вы попробуете другую операцию после закрытия базы данных, она предоставит вам это исключение.

В документации говорится:

Освобождает ссылку на объект, закрывает объект...

Кроме того, проверьте Закрытое исключение Android Sq Lite о комментарии разработчика Android Framework, в котором говорится, что закрыть соединение с базой данных не требуется.

  • 2
    Я использовал синхронизированное слово в getInstance (), как показано в этом блоге: touchlabblog.tumblr.com/post/24474750219/…, а затем удалил все db.close (). Кажется, что работает нормально. Я также читал здесь stackoverflow.com/questions/6608498/…, который на самом деле не является необходимым, чтобы закрыть его. В любом случае документация не ясна.
  • 0
    У меня был ненужный db.close () в моем классе DbProvider. И это только дало ошибку в Samsung Galaxy 3.
Показать ещё 5 комментариев
13

В настоящее время у меня такая же проблема. Выбрав 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, который он хранит, а затем поток Сначала завершил свою работу и вызвал SQLiteDatabase.close(), теперь поток объектов SQLiteDatabase B имеет закрытый, поэтому любые последующие вызовы операций db или вызовы метода курсора будут вызывать исключение.

  • 4
    Итак, каково решение для 2-го варианта? (многопоточность)
  • 0
    Я думаю, что мой сбой также из-за многопоточности. Я делаю несколько db.close() в службе, но есть поток обработчика websocket, который во входящем сообщении также вызывает getWritableDatabase() , пытается удалить элемент из БД, а затем также вызывает db.close() , До того, как обработчик websocket достигнет db.delete() сервис, скорее всего, вызвал db.close() в своей переменной db , что заставило поток websockets работать при закрытом соединении с базой данных.
Показать ещё 3 комментария
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.

Я знаю, это не ответ, но это может помочь. Я буду продолжать тестирование.

  • 0
    Кстати, я использую эту изолированную модель вызовов БД open-read-close во всем моем коде. Это работает везде, где еще
2
//close database after cursor is closed like:

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

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

cursor.close();
database.close();
  • 0
    Пожалуйста, отредактируйте с дополнительной информацией. Ответы «только код» и «попробуй это» не приветствуются, поскольку они не содержат контента для поиска и не объясняют, почему кто-то должен «попробовать это».
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();
                    }
Я использую return before db.beginTransaction(), мой код возвращается до beginTransaction, но в конечном итоге endTransaction. если вы не начнетеTransaction и endTransaction, появится исключение.

поэтому проверьте свой код о db.beginTransaction и endTransaction.

0

Возможно, вы закрываете базу данных до доступа к базе данных из своего приложения.

Вам нужно изменить getProfile() на

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

  try {
    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();
  finally {
    db.close();
  }
  return profile;
}

Ещё вопросы

Сообщество Overcoder
Наверх
Меню