studio oncreateloader loaders loadermanager getloadermanager android loader

oncreateloader - Android: LoaderCallbacks.OnLoadFinished llamado dos veces



loading android (10)

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 forma, el cargador se comporta como se esperaba, lo que da como resultado una única llamada onLoadFinished .
Ya no se usa en la rotación, por lo que si quieres los datos del cargador, puedes mantenerlo en tu fragmento, por ejemplo anulando en onSaveInstanceState instancia.

Editar:
Me acabo de dar cuenta de que no se llamará a onLoadFinished si la rotación ocurre durante loadInBackground del cargador. Para solucionarlo, aún necesitarás llamar a initLoader después de la rotación si los datos del cargador aún no están disponibles.

Espero que ayude.

Noté una situación extraña con los cargadores y fragmentos de Android. Cuando invoco LoaderManager.initLoader () después de la orientación, no se llama a cambio onLoadFinished (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 para publicar en grupos de google que describen la misma situación https://groups.google.com/forum/?fromgroups#!topic/android-developers/aA2vHYxSskU . Escribí una aplicación de muestra en la que solo inicié Cargador simple en Fragment.onActivityCreated () para comprobar si esto sucede y lo hace. ¿Alguien notó esto?


Como todas las búsquedas de este tema inevitablemente terminan aquí, solo quería agregar mi experiencia. Como dijo @jperera, el culpable fue que LoaderManager llamará a 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 luego desplazarme al lado de él de nuevo provocaría que mi viejo fragmento comenzara a crearse a sí mismo.

Dado que colocar initLoader () dentro de onCreate () también provoca devoluciones de llamada dobles, coloqué el initLoader () dentro onResume (). Pero la secuencia de eventos termina siendo onCreate (), LoaderManager llama devoluciones de llamada ya que existen cargadores, entonces se llama a onResume (), lo que activa otra secuencia de initLoader () y onLoadFinished (). IE, otra doble devolución de llamada.

solución

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


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

La única diferencia es que en Fragment.onStart comprueba si mLoaderManager! = Null. Lo que esto significa es si llama a getLoadManager antes de onStart, como en onActivityCreated, obtendrá / create load manager y se llamará. Para evitar esto, necesita llamarlo más tarde, como en onResume.


Este problema se me manifestó cuando un CursorLoader devolvió 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 el movimiento de initLoader () en onResume puede funcionar, lo que pude hacer fue eliminar el Loader cuando terminé con él:

Para iniciar el cargador (en mi onCreate):

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

Luego, cuando haya terminado con esto (básicamente al final de onLoadFinished)

getLoaderManager().destroyLoader(MUSIC_LOADER_ID);

Esto parece comportarse como se esperaba, sin llamadas adicionales.


Me he enfrentado a este problema. Pero mientras solía llamar al destroyloader(YOUR_ID) en los métodos de acabado de la loader. entonces el cargador no vuelve a llamar a la tarea backgrdound dos veces.


Puede poner el método initLoader () dentro de la devolución de llamada de su Fragment onResume (); entonces el Cargador onLoadFinished () ya no será llamado dos veces más.

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


Resolví el problema de que onLoadFinished fuera llamado dos veces como este. En tu Fragment.onActivityCreated () inicia tu cargador 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 devoluciones de llamada habituales 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) { ... ... } };


Si implementa AppCompatActivity, compruebe que está utilizando getSupportLoaderManager () en todos los casos (destroyLoader / initLoader, etc.). Había utilizado erróneamente getSupportLoaderManager () junto con un getLoaderManager () y sufrí el mismo problema.


También puede comparar el objeto de datos en onLoadFinished (Loader loader, Object data). Si el objeto de datos coincide con uno que ya tiene, simplemente no puede hacer nada cuando se invoca onLoadFinished. Por ejemplo:

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


La documentación de initLoader dice:

Si en el momento de la llamada, la persona que llama se encuentra en su estado de inicio, y el cargador solicitado ya existe y ha generado sus datos, entonces devuelva la llamada a la función de carga final (cargador, D)

Te sugiero que implementes algo así como la función onStartLoading en esta sample

Para una prueba rápida, puede intentar:

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

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

De cualquier forma, si adjuntas un código intentaré darte más ayuda.