Ошибка Cursor.moveToNext

#java #android #sqlite #android-contentprovider #android-cursor

#java #Android #sqlite #android-contentprovider #android-курсор

Вопрос:

Я время от времени вижу отчет о сбое для этого:

 Fatal Exception: java.lang.IllegalStateException: Couldn't read row 1127, col 0 from CursorWindow.  Make sure the Cursor is initialized correctly before accessing data from it.
   at android.database.CursorWindow.nativeGetLong(CursorWindow.java)
   at android.database.CursorWindow.getLong(CursorWindow.java:511)
   at android.database.AbstractWindowedCursor.getLong(AbstractWindowedCursor.java:75)
   at android.database.AbstractCursor.moveToPosition(AbstractCursor.java:220)
   at android.database.AbstractCursor.moveToNext(AbstractCursor.java:245)
   at android.database.CursorWrapper.moveToNext(CursorWrapper.java:166)
   at com.anthonymandra.util.ImageUtils.cleanDatabase(SourceFile:381)
  

По-видимому, moveToNext происходит сбой в середине цикла (обратите внимание на строку 1127). Цикл удаляет записи, представляющие файлы, которые больше не могут быть найдены.

 final ArrayList<ContentProviderOperation> operations = new ArrayList<>();

try( Cursor cursor = c.getContentResolver().query(Meta.CONTENT_URI, null, null, null, null))
{
    if (cursor == null)
        return;

    final int uriColumn = cursor.getColumnIndex(Meta.URI);
    final int idColumn = cursor.getColumnIndex(BaseColumns._ID);

    while (cursor.moveToNext())
    {
        String uriString = cursor.getString(uriColumn);
        if (uriString == null)  // we've got some bogus data, just remove
        {
            operations.add(ContentProviderOperation.newDelete(
                    Uri.withAppendedPath(Meta.CONTENT_URI, cursor.getString(idColumn))).build());
            continue;
        }
        Uri uri = Uri.parse(uriString);
        UsefulDocumentFile file = UsefulDocumentFile.fromUri(c, uri);
        if (!file.exists())
        {
            operations.add(ContentProviderOperation.newDelete(Meta.CONTENT_URI)
                    .withSelection(getWhere(), new String[]{uriString}).build());
        }
    }
}

c.getContentResolver().applyBatch(Meta.AUTHORITY, operations);
  

Есть идеи, как курсор может выйти из строя в середине цикла подобным образом?

Комментарии:

1. это ваш полный logcat?

Ответ №1:

Похоже, вы выполняете довольно большой запрос: по крайней мере, 1127 строк и для всех возможных столбцов (несмотря на то, что вы используете только два из них). И во время вашей работы с этим Cursor вы выполняете дисковый ввод-вывод и / или IPC обратно к ContentProvider , предполагая, что это UsefulDocumentFile связано с Android DocumentFile .

Как отмечает Пракаш, Cursor получаемый вами ответ может содержать только подмножество информации. Как только вы попытаетесь продвинуться дальше этой точки, Cursor необходимо вернуться к источнику данных и получить следующее окно результатов. Я вижу, что вы сталкиваетесь с такого рода проблемой, если во время выполнения этой работы произошли существенные изменения в данных (например, теперь осталось меньше 1127 строк).

Я предлагаю вам:

  • Ограничьте возвращаемые столбцы нужным вам подмножеством и

  • Избегайте ввода-вывода во время цикла (например, прокручивайте Cursor для создания ArrayList<Pair> или чего-то еще, закройте Cursor , затем выполните итерацию по списку)

Комментарии:

1. Вы предлагаете мне создать массив UsefulDocumentFile (да, это DocumentFile с большим количеством улучшений). Операции уже помещены в массив и впоследствии обрабатываются пакетами. UsefulDocumentFile Создание будет выполняться в другой базе данных через SAF. Вызовет ли проблему простой ввод-вывод или IPC? Мне действительно следует использовать проекции. Игнорировал проекции на ранних этапах создания, и привычка закрепилась.

2. @Anthony: «Вы предлагаете мне создать массив UsefulDocumentFile» — это, вероятно, работает. Я не уверен, DocumentFile выполняется ли ввод-вывод в fromUri() . Если это произойдет, то я бы придержал Uri , ожидая создания DocumentFile экземпляров, пока вы не закончите с Cursor . «Вызовет ли проблему простой ввод-вывод или IPC?» — ну, это замедляет работу, требуя от вас дольше держать ваш Cursor открытым, повышая вероятность проблем с Windows. Для небольших запросов, где вы наверняка находитесь в пределах одного окна, это не имеет большого значения.

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

4. @Anthony: В общем, да. Это особенно верно, когда вы запрашиваете какую-либо стороннюю программу ContentProvider , просто потому, что есть больше вещей, которые могут пойти не так (например, сбой приложения).

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

Ответ №2:

возможно, вы скопировали файл в базе данных, поэтому вы получаете java.lang.IllegalStateException

  UsefulDocumentFile file = UsefulDocumentFile.fromUri(c, uri);
  

Android SQLite возвращает строки в окнах курсора, максимальный размер которых составляет 2 МБ, как указано
config_cursorWindowSize.Если ваша строка превысит это ограничение, вы получите эту ошибку.

Сохраняйте файлы в файловой системе, а пути — в базе данных.