Ошибка Не удается выполнить эту операцию, поскольку пул соединений был закрыт
Я работаю с 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;
}
Я пробовал искать в Интернете много часов, но никакого результата. Пожалуйста, помогите мне исправить это. Любая помощь будет оценена. Спасибо!
Ответы
Ответ 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