Подтвердить что ты не робот

Ошибка Не удается выполнить эту операцию, поскольку пул соединений был закрыт

Я работаю с sqlite db и использую какой-то код Alex LockWood Правильное управление вашей базой данных SQLite

Он работает очень хорошо, но иногда я получил ошибку "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;
    }

Я пробовал искать в Интернете много часов, но никакого результата. Пожалуйста, помогите мне исправить это. Любая помощь будет оценена. Спасибо!

4b9b3361

Ответ 1

Я также занимаюсь этой проблемой. У меня есть идея причины, и мне бы хотелось услышать мнения экспертов по этому поводу. Я делаю немало предположений и прихожу к некоторым выводам, не подкрепленным доказательством....

 

Я думаю, что исключение выбрасывается на строку "return StadiumStatus". Пара строк выше, вы назначаете эту переменную члену курсора. Я считаю, что вы фактически назначаете имя стадионStatus указывать на переменную int, которая существует только внутри курсора. Другая возможность (огромный скачок логики здесь) заключается в том, что элемент, возвращаемый Cursor.getInt(), фактически является указателем на элемент (метод?) В открытой базе данных (который находится в памяти, потому что он открыт.) В любом случае, когда вы закрываете курсор и/или базу данных, а затем пытаетесь вернуть стадионStatus, int указывает на адрес в курсоре или передается курсором, пытается следовать за ним и тупик на закрытом курсоре или закрытой базе данных (один из них - "пул данных", из которого вы рисуете.)

 

Если я прав, я думаю, что лучшее исправление для них было бы заменено:

"StadiumStatus = cur.getInt(11);"

с:

"StadiumStatus = новый Integer (cur.getInt(11));"

тем самым создавая новый Integer в памяти, который vm может тривиально использовать для int и который зависит только от его традиционной возможности для доступности.

 

Имеет ли смысл этот ответ? Я уверен, что этот вопрос давно уже прошел мимо этой проблемы, но я хотел бы сказать, что ответил на вопрос о переполнении стека (или чтобы узнать, почему я ошибаюсь, и лучше понимаю курсоры и базы данных).

 

Изменить: эти ответы подразумевают, что я могу быть прав:
    Ошибка Android: не удается выполнить эту операцию, потому что пул подключений закрыт
    Будет ли курсор оставаться в живых после закрытия базы данных?
 Во втором ответе пользователь DeeV, имеющий огромную репутацию, называет "постоянные соединения" курсоров, подразумевая, что это не простая коллекция возвращаемых значений из БД.

Ответ 2

В коде, который вы используете:

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

Лучшей практикой является сначала закрыть курсор, а затем закрыть базу данных, как только вы вызываете db.close(), это сделает замораживание базы данных n закрытой и соответствующий ей указатель недействителен. Попробуйте это изменение, это сработает.

Заменить его на:

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

Ответ 3

Я обнаружил, что это исключение также возникает при использовании базы данных в многопоточности. Поэтому лучше добавить synchronized между db = helper.getXXXDatabase() и db.close()

Ответ 4

База данных, открытая классом SQLiteOpenHelper, остается открытой для жизни класса.

Таким образом, нет необходимости открывать и закрывать базу данных. Фактически, если он используется для нескольких потоков, это будет неправильно.

Мое приложение в течение некоторого времени было ошибкой базы данных, у меня есть один вспомогательный класс. Это должно быть одноэлементным, поэтому разные экземпляры SqliteDatabase не пытаются и одновременно изменять файл.

Также некоторые из аварий, которые я использовал, чтобы показать, что оба курсора и запросы могут вызывать sqlite Query, поэтому одновременный доступ к записи и записи в базу данных не имеет смысла.

По этой причине я добавил блокировку чтения/записи к помощнику и использовал этот шаблон...

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

Ответ 5

Я встретил эту проблему и решил ее здесь java.lang.IllegalStateException: не удается выполнить эту операцию, потому что пул подключений закрыт. В моем случае я проанализировал структуру базы данных Android. Настоящая причина mime заключается в том, что я использую SQLiteDatabase во многих объектах хранилища, которые используются для сохранения и запроса данных. Поскольку SQLiteOpenHelper является singleton, SQLiteDatabase также является одноэлементным. Даже я добавляю синхронизированный в одном магазине, он все еще разблокирован в другом магазине. Поэтому, если я забуду удалить SQLiteDatabase.close() в одном месте, это приведет к закрытию SQLiteConnectionPool. Кроме того, я также добавил некоторые методы для выпуска соединения SQLite. Надеюсь, что это поможет.

Ответ 6

Вызов

db.open()

после этой строки:

SQLiteDatabase db = this.getReadableDatabase();

Однако я лично не знаю, как this.getReadableDatabase() делает именно это, но я бы просто сделал свой собственный адаптер базы данных. См. Эту ссылку для получения более подробной информации: http://www.vogella.com/articles/AndroidSQLite/article.html