База данных 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));
}

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

Ответ 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();
    }
  }
}