Loader entrega el resultado al fragmento incorrecto

Tengo una actividad con las lengüetas de deslizamiento usando las lengüetas de ActionBar, basadas en el ejemplo androide del revelador .

Cada ficha muestra un fragmento, y cada fragmento (en realidad, un SherlockFragment) carga un tipo diferente de solicitud de api remota a través de un AsyncTaskLoader personalizado.

El problema es que si toca una pestaña para mover 2 pestañas / páginas mientras el fragmento de la pestaña que está dejando (el fragmento antiguo) está cargando un resultado, ese resultado se entrega al fragmento de la pestaña a la que se mueve (el Nuevo fragmento). En mi caso, esto conduce a una ClassCastException, ya que los resultados esperados son de tipos incompatibles.

En código, la esencia de la situación es:

Cargadores:

public class FooLoader extends AsyncTaskLoader<Foo> public class BarLoader extends AsyncTaskLoader<Bar> 

Fragmentos:

 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(); } ... } 

El código de gestión de pestañas es como en el ejemplo antes mencionado . Hay una tercera pestaña entre las pestañas Foo y Bar (llámala Baz). Cuando saltamos de la pestaña Foo a la pestaña Barra tocando la pestaña Barra después de FooFragment ha llamado initLoader en su LoaderManager pero antes de que FooFragment.onLoadFinished sea llamado, terminamos con un ClassCastException en una llamada a 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) ... 

¿Por qué está sucediendo esto, y cómo se puede prevenir? Se ve desde los registros de depuración como el mismo LoaderManager se está reutilizando en el fragmento Bar (aunque el fragmento Baz tiene su propio), pero no sé por qué debería suceder.

Actualización: El uso de diferentes identificadores de cargador en cada fragmento elimina el bloqueo (o parece que no sé realmente por qué), pero prefiero no hacerlo. En uno de los fragmentos realmente crear IDs dinámicamente y no quiero asumir que no habrá ninguna colisión. Además, esa solución es extraña para mí: los identificadores del cargador deben ser locales para cada fragmento (de lo contrario, ¿por qué puedo tener cargadores con los mismos identificadores en diferentes fragmentos en circunstancias normales?)

Parece que también puedo eliminar el bloqueo llamando a setOffscreenPageLimit(2) en mi ViewPager, para que la vista de Foo no se descarte cuando cambiamos a la vista de barra. Pero esto es una solución, no una solución general.

Código completo: he creado una aplicación de ejemplo que demuestra el error . Incluye un script monkeyrunner para forzar el error (aunque puede que no funcione para todos los tamaños de pantalla).

No utilice 0 como ID. En la medida en que LoaderManager sabe que están destinados a ser el mismo cargador.

Puede definir ID únicos en un archivo de recursos XML

 <item type="id" name="loader_foo" /> <item type="id" name="loader_bar" /> 

Y acceder a ellos desde R.

 loaderManager.initLoader(R.id.loader_foo, null, new LoaderCallbacks(){}); 

La documentación de LoaderManager dice que "los identificadores están delimitados a una instancia LoaderManager en particular". Y las instancias de LoaderMananger están vinculadas a Activities .

Para generar identificaciones únicas, puede asignarlas manualmente identificaciones que tengan grandes diferencias entre ellas.

 private static final int LOADER_FOO = 1; private static final int LOADER_BAR = 100; for(int i = 0; i < 10; ++i){ loaderManager.initLoader(LOADER_FOO + i, null, new LoaderCallbacks(){}); } 

Puede evitar este problema llamando a initLoader en initLoader en lugar de en onCreate , como señaló Alex Lockwood en los comentarios de la pregunta. Código modificado a continuación.

Fragmentos corregidos:

 public class FooFragment extends Fragment implements LoaderManager.LoaderCallbacks<Foo> { ... @Override public void onActivityCreated(Bundle savedInstanceState) { super.onActivityCreated(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 onActivityCreated(Bundle savedInstanceState) { super.onActivityCreated(savedInstanceState); getLoaderManager().initLoader(0, null, this); } public Loader<Bar> onCreateLoader(int id, Bundle args) { return new BarLoader(); } ... } 

Recientemente estuve trabajando con SimpleCursorAdapter y cargadores para mostrar datos de tablas sql lite. Pasé mucho tiempo depurando un error "no hay tal columna '_id'." Que ocurrió en el 2do fragmento de onLoadFinished (). Mi primera tabla tenía la columna '_id' pero mi didnt de la 2da tabla, que me llevó a creer que el cargador está entregando al fragmento incorrecto. Tan apenas en caso de que usted hiciera algo similar, cerciórese de que sus tablas del sql tengan el '_id' columna primero. Espero que esto ayude a alguien que tenía el mismo problema que yo.

  • Fragmentos anidados utilizando la biblioteca de soporte v4 revisión 11
  • AdMob AdView y ViewPager?
  • Desplazamiento no funciona en android ViewPager con FragmentPagerAdapter
  • ¿Existe un DotsPageIndicator predeterminado para Android móvil (no usar)?
  • ¿Cómo puedo añadir un fragmento a un ViewPager? AddView bloquea mi aplicación
  • No se puede cerrar NavigationDrawer con desplazamiento de derecha a izquierda
  • 2 ViewPager no se desplaza en Android 2.3
  • ViewPager tiene datos de offset incorrectos después de girar la pantalla cuando la página actual no es la primera
  • Cómo cambiar la fuente de un pagertitlestrip
  • FragmentStatePagerAdapter onPageSelected evento
  • Ver ciclo de vida del paginador y del fragmento
  • FlipAndroid es un fan de Google para Android, Todo sobre Android Phones, Android Wear, Android Dev y Aplicaciones para Android Aplicaciones.