Использование курсора, возвращенного из LoaderManager в AsyncTask

У меня есть курсор, возвращенный при обратном вызове onLoadFinished (из LoaderManager.LoaderCallbacks), и я хочу выполнить некоторую (возможно, дорогостоящую) постобработку этого курсора. Итак, я запускаю AsyncTask, который использует этот курсор. Однако я получаю периодические сбои с этим исключением:

android.database.StaleDataException: Attempted to access a cursor after it has been closed.

Я подозреваю, что это происходит потому, что курсор (управляемый загрузчиком в потоке пользовательского интерфейса) закрывается до того, как фоновый поток завершит работу с ним, поскольку это управляемый курсор. Вот некоторый перефразированный код:

private class LoaderListener implements LoaderManager.LoaderCallbacks<Cursor> {
    @Override
    public void onCreateLoader(int d, Bundle args) {
        return new CursorLoader(context, uri, projection, selection, selectionArgs, sortOrder);
    }

    @Override
    public void onLoadFinished(Loader<Cursor> loader, Cursor cursor) {
        processCursor(cursor)    
    }
}

private void processCursor(final Cursor cursor) {
    new AsyncTask<Void, Void, Result> {
        @Override
        Result doInBackground(Void... params) {
            while(cursor.isAfterLast() == false) {
                // doing some costly things with cursor
            }
        }
    }.execute();
}

Возможно ли,

  1. Как-нибудь пометьте курсор, чтобы предотвратить его закрытие из потока пользовательского интерфейса.

  2. Сообщите менеджеру, что курсор все еще используется.

  3. Клонируйте его, чтобы клонированный экземпляр не был закрыт менеджером.

  4. Другое, еще лучшее решение?

Однако выполнение этой постобработки в потоке пользовательского интерфейса абсолютно не вариант, поскольку это может быть очень дорогостоящим.


person Luiz Scheidegger    schedule 08.06.2012    source источник
comment
Вы всегда можете пропустить CursorLoader и сделать начальный запрос + дорогостоящую обработку в AsyncTask.   -  person user    schedule 09.06.2012
comment
вы используете этот курсор для чего-то еще?   -  person njzk2    schedule 16.05.2014


Ответы (2)


Можно ли как-то пометить курсор, чтобы предотвратить его закрытие из потока пользовательского интерфейса?

Нет (ну не без переписывания внутренних API).

Можно ли уведомить менеджера о том, что курсор все еще используется?

Тот же ответ, что и выше.

Можно ли его клонировать, чтобы клонированный экземпляр не закрывался менеджером?

Звучит как-то запутанно... и все еще есть шанс, что LoaderManager закроет курсор до того, как вы закончите его клонирование.

Есть ли лучшее решение?

Да. Запросите новый курсор вместо того, чтобы пытаться повторно использовать тот, который вы передаете в LoaderManager.

person Alex Lockwood    schedule 09.06.2012
comment
Можете быть более конкретными? Насколько я понимаю ваш ответ, мне все равно придется обработать новый курсор, что приведет к той же проблеме...? - person Luiz Scheidegger; 09.06.2012
comment
Могу я спросить, что вы пытаетесь сделать? Почему ваша постобработка связана с жизненным циклом LoaderManager? Почему бы просто не рассматривать их отдельно? Так было бы намного чище... - person Alex Lockwood; 09.06.2012

Чувак, я столкнулся с той же проблемой. Способ состоит в том, чтобы отменить асинхронную задачу в методе активности onDestroy.

private YourAsyncTask asyncTask

@Override
protected void onDestroy(){
    super.onDestroy();
    asyncTask.cancel(true);
}
person Amio.io    schedule 16.05.2014
comment
Я сталкиваюсь с той же проблемой, что и Чепанг. Ни один из ответов на самом деле не дает решения. Предложение запросить другой курсор кажется каким-то абсурдным. Я полагаю, вы могли бы создать MatrixCursor из оригинала, который представляет собой некоторую форму клонирования. Отмена AsyncTask также не имеет смысла. - person Theo; 05.07.2018