У меня есть подпрограмма, которая запускает разные запросы к базе данных SQLite много раз в секунду. Через некоторое время я получу ошибку
"android.database.CursorWindowAllocationException: - Cursor window allocation of 2048 kb failed. # Open Cursors = "
отображаются в LogCat.
У меня было использование памяти журнала приложений, и действительно, когда использование достигает определенного предела, я получаю эту ошибку, подразумевая, что она заканчивается. Моя интуиция подсказывает мне, что механизм базы данных создает новый буфер (CursorWindow) каждый раз, когда я запускаю запрос, и хотя я отмечаю курсоры .close(), ни сборщик мусора, ни SQLiteDatabase.releaseMemory()
достаточно быстро освобождают память, Я думаю, что решение может заключаться в том, чтобы "заставить" базу данных всегда записывать в один и тот же буфер, а не создавать новые, но мне не удалось найти способ сделать это. Я попытался создать экземпляр собственного CursorWindow и попытался настроить его, а SQLiteCursor - безрезультатно.
¿Любые идеи?
EDIT: повторите пример запроса кода от @GrahamBorland:
public static CursorWindow cursorWindow = new CursorWindow("cursorWindow");
public static SQLiteCursor sqlCursor;
public static void getItemsVisibleArea(GeoPoint mapCenter, int latSpan, int lonSpan) {
query = "SELECT * FROM Items"; //would be more complex in real code
sqlCursor = (SQLiteCursor)db.rawQuery(query, null);
sqlCursor.setWindow(cursorWindow);
}
В идеале я хотел бы иметь возможность .setWindow()
перед тем, как дать новый запрос, и каждый раз, когда я получаю новые данные, данные помещаются в один и тот же CursorWindow
.
Чаще всего причиной этой ошибки являются не закрытые курсоры. Убедитесь, что вы закрыли все курсоры после их использования (даже в случае ошибки).
Cursor cursor = null;
try {
cursor = db.query(...
// do some work with the cursor here.
} finally {
// this gets called even if there is an exception somewhere above
if(cursor != null)
cursor.close();
}
Если вам нужно прорыть значительное количество кода SQL, вы можете ускорить отладку, поместив следующий фрагмент кода в свой MainActivity, чтобы включить StrictMode. Если обнаружены пропущенные объекты базы данных, ваше приложение теперь будет разбиваться с информацией о журнале, точно указывая, где находится ваша утечка. Это помогло мне найти мошенника в считанные минуты.
@Override
protected void onCreate(Bundle savedInstanceState) {
if (BuildConfig.DEBUG) {
StrictMode.setVmPolicy(new StrictMode.VmPolicy.Builder()
.detectLeakedSqlLiteObjects()
.detectLeakedClosableObjects()
.penaltyLog()
.penaltyDeath()
.build());
}
super.onCreate(savedInstanceState);
...
...
if (BuildConfig.DEBUG) {...}
внутри блока static {...} моего основного класса.
Я только что испытал эту проблему - и предлагаемый ответ не закрывать курсор в то время как действительный, не был тем, как я его исправил. Моя проблема заключалась в закрытии базы данных, когда SQLite пыталась переместить курсор. Я бы открыл базу данных, запросил базу данных, чтобы получить указатель на набор данных, закрыть базу данных и перебрать курсор. Я заметил, что всякий раз, когда я нажимаю определенную запись в этом курсоре, мое приложение рушится с той же ошибкой в OP.
Я предполагаю, что для того, чтобы курсор получал доступ к определенным записям, ему необходимо повторно запросить базу данных, и если она будет закрыта, она будет вызывать эту ошибку. Я исправил его, не закрывая базу данных, пока не выполнил всю необходимую мне работу.