IllegalStateException при перезапуске загрузчика (SQLite) из AsyncTask внутри фрагмента

Я заметил некоторые IllegalStateException сбои в моем аналитическом отчете, которые я не могу воспроизвести. Я делаю что-то структурно неправильное здесь? Я включил некоторый псевдокод, который, надеюсь, показывает скелет моего фрагмента, не будучи слишком загроможденным. Я не помню, чтобы видел эту ошибку, пока не провел рефакторинг своего кода для использования фрагментов. Ошибка возникает в AsyncTask onPostExecute. Пожалуйста, дайте мне знать, если я могу лучше объяснить свою проблему/псевдокод.

public class MyListFragment extends ListFragment 
    implements LoaderManager.LoaderCallbacks<Cursor> {

    Fragment fragment = this;
    SimpleCursorAdapter adapter;

    // onActivityCreated initializes the adapter and the loader

    public AsyncTaskLoader<Cursor> onCreateloader(int id, Bundle bundle) {
        return new CustomCursorLoader(this);
    }

    public void onLoadFinished(Loader<Cursor> loader, Cursor data) {
        adapter.swapCursor(data);
    }

    public void onLoaderReset(Loader<Cursor> loader) {
        adapter.swapCursor(null);
    }

    private class CustomCursorLoader extends CursorLoader {
        public CustomCursorLoader(Fragment fragment) {
            // get a handle to the OrmLite database helper class
            databaseHelper = (DatabaseHelper) OpenHelperManager.getHelper(fragment.getActivity(), DatabaseHelper.class);
        }

        public Cursor loadInBackground() {
            // construct the Cursor that will be used for loading results from the SQLite database
        }
    }

    private class Task extends AsyncTask {
        // pre execute

        // grab some new database results from the server and repopulate the SQLite database using OrmLite

        public void onPostExecute() {
            fragment.getLoaderManager().restartLoader(LOADER_ID, null, fragment);
        }
    }
}

Обновить

Я смог воспроизвести проблему, инициировав Task, а затем сразу же нанеся ответный удар. Когда Task завершает действие, содержащее фрагмент, становится неактивным, поэтому вызов getLoaderManager завершается ошибкой. Теперь вопрос в том, какой самый элегантный способ справиться с этим? Я полагаю, я могу просто поймать Exception.

Обновление 2

Я считаю, что могу использовать отмену, чтобы предотвратить выполнение onPostExecute. Если я это сделаю, мне, вероятно, потребуется переместить initLoader в другой метод, чем onActivityCreated, чтобы обновить загрузчик, когда пользователь вернется на страницу (поскольку результаты обновляются, а не загрузчик).

Обновление 3

Мне интересно, как упоминает Робби Понд в комментариях, не является ли использование загрузчика здесь излишним?


person theblang    schedule 25.02.2014    source источник
comment
Пожалуйста, опубликуйте вывод logcat (stacktrace) для получения дополнительной информации.   -  person stan0    schedule 25.02.2014
comment
@stan0 Хотел бы я, чтобы я мог. Я использую EasyTracker в аналитике, поэтому все, что я получаю, это имя исключения и номер строки. Я не могу воспроизвести проблему локально.   -  person theblang    schedule 25.02.2014
comment
Когда вы создаете и выполняете задачу?   -  person Robby Pond    schedule 25.02.2014
comment
@RobbyPond Если в настоящее время в базе данных нет результатов, то в onActivityCreated. В противном случае, в onOptionsItemSelected, когда они нажмут элемент панели действий обновления.   -  person theblang    schedule 25.02.2014
comment
Я не думаю, что мы можем также с количеством кода. Почему вы используете и Loader, и AsyncTask для запросов к базе данных?   -  person Robby Pond    schedule 25.02.2014
comment
@RobbyPond Я читал, что именно так я должен обновить ListView, а не notifyDataSetChanged. В какой-то момент я выполнял повторный запрос в AsyncTask, но переключился на загрузчики.   -  person theblang    schedule 25.02.2014
comment
Я смог воспроизвести ошибку! Это происходит, если я инициирую Task, а затем сразу же наношу ответный удар. Когда Задача завершается, мы больше не находимся в действии, содержащем фрагмент, поэтому getLoaderManager терпит неудачу.   -  person theblang    schedule 25.02.2014
comment
@RobbyPond После некоторого чтения и размышлений я думаю, что понимаю, что вы имеете в виду. Поскольку я уже выполняю сетевой вызов в AsyncTask, я могу просто манипулировать курсором адаптера там, а не использовать также Loader. Вам никогда не понадобится загрузчик, если вы уже обновляете данные асинхронно?   -  person theblang    schedule 26.02.2014
comment
@RobbyPond Я полностью выпотрошил Loader и просто использую swapCursor в своем AsyncTask. Вы были правы, предполагая, что я не должен использовать оба Loader and AsyncTask. Если вы хотите оставить свой комментарий в качестве ответа, я приму его.   -  person theblang    schedule 19.03.2014
comment
А что говорит IllegalStateException? Любые вызванные сообщения?   -  person Gray    schedule 30.05.2018


Ответы (1)


Некоторые из моих мыслей о вашем случае:

  1. Загрузчик имеет различные проблемы, особенно при использовании с фрагментами. Я мог бы посоветовать вам использовать ViewModel, но я думаю, что они оба основаны на одном и том же коде. Вы можете использовать обычный поток, а затем использовать что-то вроде EventBus для публикации результата, но, возможно, это излишество.

  2. Я вижу, что вы забыли установить классы как статические, что означает, что они имеют ссылку на класс хостинга.

  3. Можно ли объединить асинхронный код в один AsyncTaskLoader?

  4. Вы можете попробовать мое общее решение с использованием AsyncTaskLoader, здесь.

  5. Может быть, вместо перезагрузки сначала уничтожить, а затем снова вызвать initLoader.

  6. AsyncTask следует использовать только в небольших случаях, когда вам все равно, исчезнет ли результат (например, когда вы меняете ориентацию). Хорошим примером является список элементов для отображения.

  7. Я думаю, что Google упомянул в Google IO 2018, что проблемы с загрузчиками будут исправлены. Не уверен, что в библиотеке поддержки 28 или в библиотеке AndroidX. Предлагаю проверить. Главное помнить, что использовать его можно только в UI-потоке.

person android developer    schedule 27.05.2018