У меня есть активность с прокручиванием вкладок с помощью вкладок ActionBar на основе примера разработчика Android.
Каждая вкладка отображает фрагмент, и каждый фрагмент (на самом деле, Шерлок-Фрагмент) загружает другой вид удаленного запроса api через пользовательский AsyncTaskLoader.
Проблема заключается в том, что если вы нажмете вкладку, чтобы переместить 2 вкладки/страницы, а фрагмент для вкладки, которую вы оставляете (старый фрагмент), загружает результат, этот результат доставляется к фрагменту для вкладки, которую вы перемещаете к (новый фрагмент). В моем случае это приводит к исключению ClassCastException, поскольку ожидаемые результаты имеют несовместимые типы.
В коде суть сущности:
Погрузчики:
public class FooLoader extends AsyncTaskLoader<Foo>
public class BarLoader extends AsyncTaskLoader<Bar>
Фрагменты:
public class FooFragment extends Fragment implements LoaderManager.LoaderCallbacks<Foo> {
...
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
getLoaderManager().initLoader(0, null, this);
}
public Loader<Foo> onCreateLoader(int id, Bundle args) { return new FooLoader(); }
...
}
public class BarFragment extends Fragment implements LoaderManager.LoaderCallbacks<Bar> {
...
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
getLoaderManager().initLoader(0, null, this);
}
public Loader<Bar> onCreateLoader(int id, Bundle args) { return new BarLoader(); }
...
}
Код управления вкладками приведен в вышеупомянутом примере. Между вкладками Foo и Bar есть третья вкладка (назовите ее Baz). Когда мы переходим с вкладки Foo на вкладку "Бар", нажимая на вкладку "Бар" после того, как FooFragment вызвал initLoader в своем LoaderManager, но до того, как вызывается FooFragment.onLoadFinished, мы заканчиваем ClassCastException при вызове BarFragment.onLoadFinished:
java.lang.ClassCastException: com.example.Foo cannot be cast to com.example.Bar
at com.example.BarFragment.onLoadFinished(BarFragment.java:1)
at android.support.v4.app.LoaderManagerImpl$LoaderInfo.callOnLoadFinished(LoaderManager.java:427)
at android.support.v4.app.LoaderManagerImpl.initLoader(LoaderManager.java:562)
at com.example.BarFragment.onCreate(BarFragment.java:36)
at android.support.v4.app.Fragment.performCreate(Fragment.java:1437)
...
Почему это происходит, и как его можно предотвратить? Это выглядит из журналов отладки, так как тот же LoaderManager повторно используется в фрагменте Bar (хотя у Baz-фрагмента есть свой собственный), но я не знаю, почему это должно произойти.
Обновление: Использование разных идентификаторов загрузчика в каждом фрагменте устраняет крах (или, кажется, - я действительно не знаю почему), но я бы предпочел не делать этого. В одном из фрагментов я фактически создаю идентификаторы динамически и не хочу предполагать, что столкновения не будет. Кроме того, это решение странно для меня - идентификаторы загрузчика должны быть локальными для каждого фрагмента (в противном случае, почему я могу иметь загрузчики с одинаковыми идентификаторами в разных фрагментах при обычных обстоятельствах?)
Кажется, я также могу устранить крах, вызывая setOffscreenPageLimit(2)
на моем ViewPager, так что просмотр Foo не отбрасывается, когда мы переключаемся на представление Bar. Но это обходное решение, а не общее решение.
Полный код: Я создал пример приложения демонстрирующий ошибку. Он включает в себя monkeyrunner script для принудительной ошибки (хотя он может не работать для всех размеров экрана).