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.
- Barra de navegación de Android que cubre el contenido del visualizador
- Android: reutilizar fragmentos de ViewPager y FragmentPagerAdapter
- Cómo setCurrentPage en ViewPager según la selección del usuario y cómo sé qué artículo de la lista llamó la actividad
- Cómo alinear el centro de la pestaña activa en Android Disposición de la pestaña deslizante
- ViewPager dentro de la cuestión del fragmento
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).
- Android Poner la animación en la página de desplazamiento en la vista paginador utilizando fragmento
- Android ViewPager - Mostrar la vista previa de la página a la izquierda ya la derecha
- Fragmentos anidados - comunicación entre fragmentos secundarios en un fragmento padre en un viewpager
- Implemente arrastrar elemento a la función de borde de pantalla
- TextView dentro de ViewPager intercepta eventos de toque
- Cómo ir a la siguiente actividad después de ViewPager en android?
- Los datos del fragmento se van nulos cuando otro fragmento se carga
- Obtener Parent ViewPager Ver desde el interior Fragmento
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.
- Android findViewById () en la vista personalizada
- Fragmento / Actividad Ciclos de vida y cambio de orientación