База данных SQLite Android Database Cursor с 2048 КБ не выполнена
У меня есть подпрограмма, которая запускает разные запросы к базе данных 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
.
Ответы
Ответ 1
Чаще всего причиной этой ошибки являются не замкнутые курсоры. Обязательно закройте все курсоры после их использования (даже в случае ошибки).
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();
}
Чтобы вывести приложение из строя, когда вы не закрываете курсор, вы можете включить Строгий режим с помощью detectLeakedSqlLiteObjects
в своих приложениях onCreate
:
StrictMode.VmPolicy policy = new StrictMode.VmPolicy.Builder()
.detectLeakedClosableObjects()
.detectLeakedSqlLiteObjects()
.penaltyDeath()
.penaltyLog()
.build();
StrictMode.setVmPolicy(policy);
Очевидно, вы бы включили это только для отладочных сборок.
Ответ 2
Если вам нужно прорыть значительное количество кода 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);
...
...
Ответ 3
Я только что испытал эту проблему - и предлагаемый ответ не закрывать курсор в то время как действительный, не был тем, как я его исправил. Моя проблема заключалась в закрытии базы данных, когда SQLite пыталась переместить курсор. Я бы открыл базу данных, запросил базу данных, чтобы получить указатель на набор данных, закрыть базу данных и перебрать курсор. Я заметил, что всякий раз, когда я нажимаю определенную запись в этом курсоре, мое приложение рушится с той же ошибкой в OP.
Я предполагаю, что для того, чтобы курсор получал доступ к определенным записям, ему необходимо повторно запросить базу данных, и если она будет закрыта, она будет вызывать эту ошибку. Я исправил его, не закрывая базу данных, пока не выполнил всю необходимую мне работу.
Ответ 4
Действительно, окна курсора Android SQLite имеют максимальный размер, который может занимать 2 МБ, и все, что больше этого размера, приведет к указанной ошибке. В основном эта ошибка вызвана либо большим байтовым массивом изображений, хранящимся в виде BLOB-объектов в базе данных SQL, либо слишком длинными строками. Вот как я это исправил.
Создайте класс Java, например. FixCursorWindow и поместите ниже код в нем.
public static void fix() {
try {
Field field = CursorWindow.class.getDeclaredField("sCursorWindowSize");
field.setAccessible(true);
field.set(null, 102400 * 1024); //the 102400 is the new size added
} catch (Exception e) {
e.printStackTrace();
}
}
Теперь перейдите к вашему классу приложения (создайте его, если у вас его еще нет) и сделайте вызов FixCursorWindow следующим образом
публичный класс приложение расширяет приложение {
public void onCreate()
{
super.onCreate();
CursorWindowFixer.fix();
}
}
Наконец, убедитесь, что вы включили свой класс приложения в свой манифест в тег приложения, как это
android:name=".App">
Вот и все, теперь это должно прекрасно работать.
Ответ 5
Если вы используете Android P, вы можете создать свое собственное окно курсора, например:
if(cursor instanceof SQLiteCursor && Build.VERSION.SDK_INT >= Build.VERSION_CODES.P) {
((SQLiteCursor) cursor).setWindow(new CursorWindow(null, 1024*1024*10));
}
Это позволяет вам изменять размер окна курсора для конкретного курсора, не прибегая к отражениям.
Ответ 6
Вот @whlk ответ с Java 7 автоматическимуправлением ресурсами блока try-finally:
try (Cursor cursor = db.query(...)) {
// do some work with the cursor here.
}
Ответ 7
Это нормальное исключение, особенно когда мы используем внешний SQLite. Вы можете решить это, закрыв объект Cursor, как показано ниже:
if(myCursor != null)
myCursor.close();
Это означает, что если у курсора есть память и он открыт, то закройте его, чтобы приложение работало быстрее, все методы будут занимать меньше места, а функции, связанные с базой данных, также будут улучшены.
Ответ 8
Основная причина этой ошибки - не закрытие курсора, который открывается. Поэтому, если вы открыли курсор в любом месте программы, не забудьте закрыть его.
Например курсор c = null;............................. cursor.close() https://ranjithandroid.blogspot.com/
Ответ 9
public class CursorWindowFixer {
public static void fix() {
try {
Field field = CursorWindow.class.getDeclaredField("sCursorWindowSize");
field.setAccessible(true);
field.set(null, 102400 * 1024);
} catch (Exception e) {
e.printStackTrace();
}
}
}