Я заметил странную ситуацию с помощью Android Loaders и Fragments. Когда я вызываю LoaderManager.initLoader() после изменения ориентации onLoadFinished не вызывается (хотя документация предполагает, что я должен быть готов к этому), но он вызывается дважды после этого. Вот ссылка на сообщение в группах google, которые описывают ту же ситуацию https://groups.google.com/forum/?fromgroups#!topic/android-developers/aA2vHYxSskU. Я написал пример приложения, в котором я только запускаю простой загрузчик в Fragment.onActivityCreated(), чтобы проверить, не произошло ли это, и это произойдет. Кто-нибудь заметил это?
Android: LoaderCallbacks.OnLoadFinished вызывается дважды
Ответ 1
Вы можете поместить метод initLoader() внутри обратного вызова Fragment onResume(); то Loader onLoadFinished() больше не будет вызываться дважды.
@Override
public void onResume()
{
super.onResume();
getLoaderManager().initLoader(0, null, this);
}
Ответ 2
Эта проблема проявилась для меня с CursorLoader, возвращающим курсор, который уже был закрыт:
android.database.StaleDataException: Attempted to access a cursor after it has been closed.
Я предполагаю, что это ошибка или недосмотр. Хотя перемещение initLoader() в onResume может работать, то, что я смог сделать, это удалить Loader, когда я закончил с ним:
Чтобы запустить загрузчик (в моем onCreate):
getLoaderManager().initLoader(MUSIC_LOADER_ID, null, this);
Затем после того, как я закончил с ним (в основном в конце onLoadFinished)
getLoaderManager().destroyLoader(MUSIC_LOADER_ID);
Это похоже на поведение, как ожидалось, никаких дополнительных вызовов.
Ответ 3
Если в точке вызова вызывающий абонент находится в запущенном состоянии, а запрошенный загрузчик уже существует и сгенерировал его данные, затем callback onLoadFinished (Loader, D)
Я предлагаю вам реализовать что-то вроде функции onStartLoading в этом образце
Для быстрого тестирования вы можете попробовать:
@Override protected void onStartLoading() {
forceLoad();
}
Эта функция запуска loadInBackground, а затем onLoadFinished в фрагменте.
В любом случае, если вы присоедините некоторый код, я попытаюсь дать вам больше информации.
Ответ 4
Я решил проблему onLoadFinished, называемую дважды так. В вашем Fragment.onActivityCreated() запустите свой загрузчик, как этот
if (getLoaderManager().getLoader(LOADER_ID) == null) {
getLoaderManager().initLoader(LOADER_ID, bundle, loaderCallbacks);
} else {
getLoaderManager().restartLoader(LOADER_ID, bundle, loaderCallbacks);
}
здесь loaderCallbacks реализует ваши обычные обратные вызовы Loader
private LoaderManager.LoaderCallbacks<T> loaderCallbacks
= new LoaderManager.LoaderCallbacks<T>() {
@Override
public Loader<T> onCreateLoader(int id, Bundle args) {
...
...
}
@Override
public void onLoadFinished(Loader<T> loader, T data) {
...
...
}
@Override
public void onLoaderReset(Loader<T> loader) {
...
...
}
};
Ответ 5
Проблема состоит в том, что он дважды вызывался:
1. из Fragment.onStart
2. из FragmentActivity.onStart
Единственное различие заключается в том, что в Fragment.onStart он проверяет, является ли mLoaderManager!= null. Это означает, что если вы вызываете getLoadManager перед onStart, например, в onActivityCreated, он получит/создаст диспетчер нагрузки и он будет вызван. Чтобы этого избежать, вам нужно вызвать его позже, например, в onResume.
Ответ 6
При вызове initLoader
из onActivityCreated
вы можете обнаружить поворот:
@Override
public void onActivityCreated(Bundle savedInstanceState) {
super.onActivityCreated(savedInstanceState);
if (savedInstanceState == null) {
// fresh new fragment, not orientation/config change
getLoaderManager().initLoader(YOUR_LOADER_ID, null, mCallbacks);
}
...
}
Таким образом, загрузчик ведет себя как ожидалось, что приводит к одиночному вызову onLoadFinished
.
Он больше не называется для вращения, поэтому, если вы хотите данные загрузчика, вы можете сохранить его в своем фрагменте, например, переопределив onSaveInstanceState
.
Изменить:
Я просто понял, что onLoadFinished
не будет вызываться, если ротация происходит во время загрузки loadInBackground
. Чтобы исправить это, вам все равно нужно вызвать initLoader
после вращения, если данные из загрузчика еще не доступны.
Надеюсь, что это поможет.
Ответ 7
Вы также можете сравнить объект данных в onLoadFinished (загрузчик загрузчика, данные объекта). Если объект данных совпадает с тем, который у вас уже есть, вы можете просто ничего не делать, когда вызывается onLoadFinished. Например:
public void onLoadFinished(Loader loader, Object data) {
if(data != null && mData != data){
//Do something
}
}
Ответ 8
Поскольку все поиски этой темы неизбежно заканчиваются здесь, я просто хотел добавить свой опыт. Как сказал @jperera, виновником было то, что LoaderManager будет вызывать onLoadFinished(), если загрузчики уже существуют. В моем случае у меня были фрагменты в FragmentPager и прокрутка 2 вкладок, а затем прокрутка рядом с ним снова заставила бы мой старый фрагмент начать создавать себя.
Так как размещение initLoader() внутри onCreate() также вызывает двойные обратные вызовы, я поместил initLoader() внутри onResume(). Но последовательность событий заканчивается тем, что onCreate(), LoaderManager вызывает обратные вызовы, поскольку существуют загрузчики, затем вызывается onResume(), вызывая очередную последовательность initLoader() и onLoadFinished(). IE, еще один двойной обратный вызов.
Решение
Я нашел быстрое решение "Matt" . После того, как все ваши данные загружены (если у вас более одного загрузчика), уничтожьте все загрузчики, чтобы их обратные вызовы не назывались дополнительным временем.
Ответ 9
У меня есть эта проблема. Но я использовал для вызова destroyloader(YOUR_ID)
в методах loaderfinished. то загрузчик снова не вызывает задачу backgrdound дважды.