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?
- Boot / ScreenOn Broadcast Receiver no funciona
- Android SQLite Query, insertar, actualizar, eliminar, siempre tiene que estar en el hilo de fondo?
- Uso de Loader en API 8
- Android diseño: fondo de servicio de larga duración o AlarmManager?
- Detectar si HTC "Fast boot" está habilitado
- BroadcastReceiver no recibe BOOT_COMPLETED
- Android: errores de difusión no ordenados con GCM
- ¿Los usuarios de Samsung Glaxy S4 informan que el receptor BOOT_COMPLETED no funcionará?
- El receptor de arranque no funciona
- Cómo desplazar un ListView en android programatically a hasta gesto hacia abajo
- Dex Loader No se puede ejecutar Los archivos dex múltiples definen
- Recepción de ADB BOOT_COMPLETE
- Inflar diseño en ListItem basado en la variable específica ListItem
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.
- Diferencia entre AsyncTask y Thread / Runnable
- Abra la aplicación después de hacer clic en Notificación