Android: LoaderCallbacks.OnLoadFinished llamado dos veces

Me di cuenta de la situación extraña con Android cargadores y fragmentos. Cuando invoco LoaderManager.initLoader () después del cambio de orientación onLoadFinished no se llama (aunque la documentación sugiere que debería estar preparado para esto), pero se llama dos veces después de esto. Aquí hay un enlace a la publicación en grupos de google que describen la misma situación https://groups.google.com/forum/?fromgroups#!topic/android-developers/aA2vHYxSskU . Escribí la aplicación de muestra en la que sólo init cargador simple en Fragment.onActivityCreated () para comprobar si esto sucede y lo hace. ¿Alguien notó esto?

Puede poner el método initLoader () en la devolución de llamada onResume () de su fragmento; Entonces onLoadFinished () del cargador no será llamado dos veces más.

  @Override public void onResume() { super.onResume(); getLoaderManager().initLoader(0, null, this); } 

Este problema se manifestó para mí con un CursorLoader devolviendo un Cursor que ya estaba cerrado:

 android.database.StaleDataException: Attempted to access a cursor after it has been closed. 

Supongo que esto es un error o un descuido. Mientras que mover initLoader () en onResume puede funcionar, lo que pude hacer fue quitar el cargador cuando haya terminado con él:

Para iniciar el cargador (en mi onCreate):

  getLoaderManager().initLoader(MUSIC_LOADER_ID, null, this); 

Luego, después de terminar con él (básicamente al final de onLoadFinished)

  getLoaderManager().destroyLoader(MUSIC_LOADER_ID); 

Esto parece comportarse como se esperaba, sin llamadas extra.

La documentación de initLoader dice,

Si en el punto de llamada el llamador está en su estado iniciado y el cargador solicitado ya existe y ha generado sus datos, entonces callback onLoadFinished (Loader, D)

Le sugiero que implemente algo como la función onStartLoading en esta muestra

Para una prueba rápida puede probar:

 @Override protected void onStartLoading() { forceLoad(); } 

Esta función loadInBackground de lanzamiento y luego onLoadFinished en Fragment.

De cualquier manera, si anexas algún código intentaré darte más ayuda.

Resolví el problema de onLoadFinished ser llamado dos veces como este. En tu Fragment.onActivityCreated () init tu Loader como este

 if (getLoaderManager().getLoader(LOADER_ID) == null) { getLoaderManager().initLoader(LOADER_ID, bundle, loaderCallbacks); } else { getLoaderManager().restartLoader(LOADER_ID, bundle, loaderCallbacks); } 

Aquí loaderCallbacks implementa sus habituales devoluciones de llamada de 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) { ... ... } }; 

El problema es que llamó dos veces:
1. de Fragment.onStart
2. de FragmentActivity.onStart

La única diferencia es que en Fragment.onStart verifica si mLoaderManager! = Null. Lo que esto significa es que si llama a getLoadManager antes onStart, como en onActivityCreated, obtendrá / creará el gestor de carga y se llamará. Para evitar esto, necesitas llamarlo más tarde, como en onResume.

Al llamar a initLoader desde onActivityCreated puede detectar la rotación:

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

De esta manera el cargador se comporta como se espera resultando en una sola llamada onLoadFinished .
Ya no se llama a la rotación, así que si quieres datos del cargador, puedes mantenerlo en tu fragmento, por ejemplo reemplazando onSaveInstanceState .

Editar:
Acabo de realizar que el onLoadFinished no será llamado si la rotación sucede durante loadInBackground del cargador. Para corregir esto, todavía necesitará llamar a initLoader después de la rotación si los datos del cargador aún no están disponibles.

Espero que ayude.

También puede comparar el objeto de datos en onLoadFinished (Cargador cargador, datos de objeto). Si el objeto de datos coincide con uno que ya tiene, simplemente no puede hacer nada cuando onLoadFinished se llama. Por ejemplo:

 public void onLoadFinished(Loader loader, Object data) { if(data != null && mData != data){ //Do something } } 

Dado que toda búsqueda de este tema inevitablemente termina aquí, sólo quería añadir mi experiencia. Como dijo @ jperera, el culpable fue que LoaderManager llamará onLoadFinished () si los cargadores ya existen. En mi caso, tenía fragmentos en un FragmentPager y el desplazamiento de 2 pestañas de distancia y, a continuación, desplazamiento al lado de nuevo podría hacer que mi fragmento de edad para comenzar a crear por sí mismo.

Dado que colocar initLoader () dentro de onCreate () también causa devoluciones de llamada dobles, coloqué initLoader () en onResume (). Pero la secuencia de eventos termina siendo onCreate (), LoaderManager llama a callbacks ya que los cargadores existen, entonces onResume () es llamado, desencadenando otra secuencia initLoader () y onLoadFinished (). IE, otro doble devolución de llamada.

solución

Encontré una solución rápida de "Matt" . Después de cargar todos sus datos (si tiene más de un cargador), destruya todos los cargadores para que sus devoluciones de llamada no se llamen un tiempo extra.

Tengo que hacer frente a este problem.but i mientras se utiliza para llamar a la destroyloader(YOUR_ID) en loaderfinished métodos. Entonces el cargador no volverá a llamar a la tarea backgrdound dos veces.

  • onCreateLoader bloquea el subproceso de interfaz de usuario
  • Android cursorLoader con paginación o carga más funcionalidad?
  • ¿Cómo subir el archivo a la caja de la url?
  • ¿No es necesario android.permission.RECEIVE_BOOT_COMPLETED?
  • Problemas con las devoluciones de llamada personalizadas de AsyncTaskLoader
  • Cual es mejor, loader o AsyncTask?
  • FlipAndroid es un fan de Google para Android, Todo sobre Android Phones, Android Wear, Android Dev y Aplicaciones para Android Aplicaciones.