Diferencia entre `initLoader` y` restartLoader` en `LoaderManager`
Estoy completamente perdido respecto a las diferencias entre las initLoader
y restartLoader
de LoaderManager
:
- Ambos tienen la misma firma.
-
restartLoader
también crea un cargador, si no existe ("Inicia un nuevo o reinicia un cargador existente en este gestor").
¿Hay alguna relación entre los dos métodos? ¿La llamada restartLoader
siempre llama a initLoader
? ¿Puedo llamar a restartLoader
sin tener que llamar a initLoader
? ¿Es guardar para llamar a initLoader
dos veces para actualizar los datos? ¿Cuándo debo usar uno de los dos y (importante!) ¿Por qué?
- Ningún dispositivo de destino encontrado android studio 2.1.1
- ¿Qué es el comando adb para enumerar todos los navegadores instalados en el dispositivo android?
- Desmarcando archivos XML en objetos Java en Android?
- La pantalla táctil de adb falla en una llamada
- Aplicación para Android: ¿Llamar a AsyncTask dos veces?
- R.menu no se puede resolver
- Difusión cuando se captura la captura de pantalla en Android 4.0?
- Eclipse entra en clase en android
- ¿Botón circular material en Android?
- ¿Cuándo necesita un servidor de aplicaciones para la mensajería de Cloud Firebase?
- Cómo reducir el tamaño del archivo mp3 en Android creado con MediaRecorder
- Cómo crear un JSONObject de objetos? Androide
- Cómo establecer el idioma predeterminado para la aplicación de Android?
Para responder a esta pregunta es necesario buscar el código LoaderManager. Si bien la documentación de LoaderManager no es suficientemente clara (o no habría esta pregunta), la documentación de LoaderManagerImpl, una subclase del LoaderManager abstracto, es mucho más esclarecedora.
InitLoader
Llamar para inicializar un ID particular con un Loader. Si este identificador ya tiene un cargador asociado con él, se deja inalterado y cualquier devolución de llamada anterior se reemplazó con los recién proporcionados. Si actualmente no hay un cargador para el ID, se crea y se inicia un nuevo.
Generalmente, esta función se debe utilizar cuando un componente se está inicializando, para asegurarse de que se crea un cargador en el que se basa. Esto le permite volver a usar los datos de un cargador existente si ya hay uno, de modo que, por ejemplo, cuando una actividad se vuelve a crear después de un cambio de configuración, no es necesario volver a crear sus cargadores.
Restartloader
Llame para volver a crear el Loader asociado con un ID particular. Si actualmente hay un Loader asociado con este ID, éste se cancelará / detendrá / destruirá según corresponda. Se creará un nuevo cargador con los argumentos dados y se le entregarán los datos una vez disponibles.
[…] Después de llamar a esta función, cualquier Cargador anterior asociado con este ID será considerado inválido, y no recibirá más actualizaciones de datos de ellos.
Hay básicamente dos casos:
- El cargador con el id no existe: ambos métodos crearán un nuevo cargador por lo que no hay diferencia allí
- El cargador con el identificador ya existe: initLoader sólo reemplazará las devoluciones de llamada pasadas como parámetro, pero no cancelará o parará el cargador. Para un CursorLoader que significa que el cursor permanece abierto y activo (si ese era el caso antes de la llamada de initLoader). RestartLoader por otro lado cancelará, detendrá y destruirá el cargador (y cerrará la fuente de datos subyacente como un cursor) y creará un nuevo cargador (que también crearía un nuevo cursor y volvería a ejecutar la consulta si el cargador es un CursorLoader) .
Aquí está el código simplificado para ambos métodos:
InitLoader
LoaderInfo info = mLoaders.get(id); if (info == null) { // Loader doesn't already exist -> create new one info = createAndInstallLoader(id, args, LoaderManager.LoaderCallbacks<Object>)callback); } else { // Loader exists -> only replace callbacks info.mCallbacks = (LoaderManager.LoaderCallbacks<Object>)callback; }
Restartloader
LoaderInfo info = mLoaders.get(id); if (info != null) { LoaderInfo inactive = mInactiveLoaders.get(id); if (inactive != null) { // does a lot of stuff to deal with already inactive loaders } else { // Keep track of the previous instance of this loader so we can destroy // it when the new one completes. info.mLoader.abandon(); mInactiveLoaders.put(id, info); } } info = createAndInstallLoader(id, args, (LoaderManager.LoaderCallbacks<Object>)callback);
Como podemos ver en el caso de que el cargador no existe (info == null) ambos métodos creará un nuevo cargador (info = createAndInstallLoader (…)). En caso de que el cargador ya existe, initLoader solo reemplaza las devoluciones de llamada (info.mCallbacks = …) mientras restartLoader inactiva el antiguo cargador (será destruido cuando el nuevo cargador termine su trabajo) y luego crea un nuevo.
Así, dijo que ahora está claro cuándo usar initLoader y cuándo usar restartLoader y por qué tiene sentido tener los dos métodos. InitLoader se utiliza para asegurar que hay un cargador inicializado. Si no existe, se crea una nueva, si ya existe, se vuelve a usar. Siempre usamos este método A MENOS que necesitamos un nuevo cargador porque la consulta a ejecutar ha cambiado (no los datos subyacentes sino la consulta real como en la instrucción SQL para un CursorLoader), en cuyo caso llamaremos restartLoader.
El ciclo de vida de Actividad / Fragmento no tiene nada que ver con la decisión de usar uno u otro método (y no hay necesidad de mantener un registro de las llamadas usando una bandera de un solo disparo como sugirió Simon). Esta decisión se basa únicamente en la "necesidad" de un nuevo cargador. Si queremos ejecutar la misma consulta que usamos initLoader, si queremos ejecutar una consulta diferente usamos restartLoader. Siempre podríamos usar restartLoader pero eso sería ineficiente. Después de una rotación de pantalla o si el usuario se aleja de la aplicación y vuelve más tarde a la misma Actividad normalmente queremos mostrar el mismo resultado de la consulta y por lo que restartLoader innecesariamente volverá a crear el cargador y descartar el resultado de la consulta subyacente .
Es muy importante entender la diferencia entre los datos que se cargan y la "consulta" para cargar esos datos. Supongamos que usamos un CursorLoader consultando una tabla para órdenes. Si se agrega un nuevo pedido a la tabla, CursorLoader utiliza onContentChanged () para informar al ui que desea actualizar y mostrar el nuevo pedido (no es necesario utilizar restartLoader en este caso). Si queremos mostrar sólo órdenes abiertas necesitamos una nueva consulta y usaremos restartLoader para devolver un nuevo CursorLoader que refleje la nueva consulta.
¿Hay alguna relación entre los dos métodos?
Ellos comparten el código para crear un nuevo cargador, pero hacen cosas diferentes cuando ya existe un cargador.
¿La llamada restartLoader siempre llama a initLoader?
No, nunca lo hace.
¿Puedo llamar a restartLoader sin tener que llamar a initLoader?
Sí.
¿Es seguro llamar a initLoader dos veces para actualizar los datos?
Es seguro llamar a initLoader dos veces, pero no se actualizarán los datos.
¿Cuándo debo usar uno de los dos y (importante!) ¿Por qué?
Eso debería (espero) ser claro después de mis explicaciones anteriores.
Cambios en la configuración
Un LoaderManager conserva su estado a lo largo de los cambios de configuración (incluidos los cambios de orientación), por lo que pensaría que no nos queda nada que hacer. Piensa otra vez…
En primer lugar, un LoaderManager no retiene las devoluciones de llamada, por lo que si no hace nada, no recibirá llamadas a los métodos de devolución de llamada como onLoadFinished () y similares, y es muy probable que rompa su aplicación. Por lo tanto, tenemos que llamar al menos initLoader para restaurar los métodos de devolución de llamada (un restartLoader es, por supuesto, posible también). La documentación establece:
Si en el punto de llamada la persona que llama está en su estado iniciado, y el cargador solicitado ya existe y ha generado sus datos, entonces callback onLoadFinished (Loader, D) será llamado inmediatamente (dentro de esta función) […].
Esto significa que si llamamos a initLoader después de un cambio de orientación, obtendremos una llamada onLoadFinished de inmediato porque los datos ya están cargados (asumiendo que era el caso antes del cambio). Mientras que suena hacia adelante puede ser difícil (no todos nos encanta Android …).
Tenemos que distinguir entre dos casos:
- Maneja los cambios de configuración por sí mismo: este es el caso de los fragmentos que utilizan setRetainInstance (true) o de una actividad con la etiqueta android: configChanges en el manifiesto. Estos componentes no recibirán una llamada onCreate después de, por ejemplo, una rotación de pantalla, así que recuerde llamar a initLoader / restartLoader en otro método de devolución de llamada (por ejemplo, en onActivityCreated (Bundle)). Para poder inicializar el cargador (s), los identificadores del cargador necesitan ser almacenados (por ejemplo, en una lista). Debido a que el componente se conserva a través de los cambios de configuración, podemos pasar por encima de los identificadores de cargador existentes y llamar a initLoader (loaderid, …).
- No maneja los cambios de configuración por sí mismo: En este caso, los cargadores se pueden inicializar en onCreate, pero tenemos que conservar manualmente los identificadores del cargador o no podremos hacer las llamadas initLoader / restartLoader necesarias. Si los IDs se almacenan en una ArrayList, haríamos una
OutState.putIntegerArrayList (loaderIdsKey, loaderIdsArray) en onSaveInstanceState y restaurar los identificadores en onCreate: loaderIdsArray = savedInstanceState.getIntegerArrayList (loaderIdsKey) antes de realizar las llamadas initLoader.
Llamar a initLoader
cuando ya se ha creado el cargador (esto suele suceder después de cambios de configuración, por ejemplo) le dice al LoaderManager que entregue los datos más recientes del cargador a onLoadFinished
inmediatamente. Si el cargador no ha sido creado (cuando la actividad / fragmento se inicia por primera vez, por ejemplo), la llamada a initLoader
le dice al LoaderManager que llame a onCreateLoader
para crear el nuevo Loader.
Llamar a restartLoader
destruye un cargador ya existente (así como cualquier dato existente asociado con él) y le dice al LoaderManager que llame a onCreateLoader
para crear el nuevo cargador e iniciar una nueva carga.
La documentación es bastante clara sobre esto también:
-
initLoader
garantiza que un Loader se inicialice y esté activo. Si el cargador ya no existe, se crea uno y (si la actividad / fragmento se inicia actualmente) inicia el cargador. De lo contrario, se reutiliza el último cargador creado. -
restartLoader
inicia un nuevo o reinicia un cargador existente en este gestor, registra las devoluciones de llamada y, si la actividad / fragmento está iniciado, comienza a cargarlo. Si un cargador con el mismo identificador se ha iniciado previamente, se destruirá automáticamente cuando el nuevo cargador termine su trabajo. La devolución de llamada se entregará antes de que el cargador antiguo se destruya.
Recientemente he golpeado un problema con varios gestores de cargador y cambios en la orientación de la pantalla y me gustaría decir que después de un montón de prueba y error, el siguiente patrón funciona para mí en ambas Actividades y Fragmentos:
onCreate: call initLoader(s) set a one-shot flag onResume: call restartLoader (or later, as applicable) if the one-shot is not set. unset the one-shot in either case.
(En otras palabras, establecer alguna bandera para que initLoader siempre se ejecuta una vez & que restartLoader se ejecuta en el segundo y pases posteriores a través de onResume )
Además, recuerde asignar diferentes IDs para cada uno de sus cargadores dentro de una Actividad (lo cual puede ser un problema con fragmentos dentro de esa actividad si no tiene cuidado con su numeración)
Intenté usar initLoader solamente …. no parecía trabajar con eficacia.
InitLoader probado en onCreate con args nulo (los documentos dicen que esto está bien) y restartLoader (con argumentos válidos) en onResume …. los docs están equivocados & initLoader lanza una excepción nullpointer.
Intentado restartLoader sólo … funciona por un tiempo, pero sopla en la 5 ª o 6 ª re-orientación de la pantalla.
InitLoader intentado en onResume ; Nuevamente funciona durante un tiempo y luego sopla. (Específicamente el mensaje "Llamado doRetain cuando no se inicia:" … error)
Intentó lo siguiente: (extracto de una clase de portada que tiene el identificador del cargador pasado en el constructor)
/** * start or restart the loader (why bother with 2 separate functions ?) (now I know why) * * @param manager * @param args * @deprecated use {@link #restart(LoaderManager, Bundle)} in onResume (as appropriate) and {@link #initialise(LoaderManager, Bundle)} in onCreate */ @Deprecated public void start(LoaderManager manager, Bundle args) { if (manager.getLoader(this.id) == null) { manager.initLoader(this.id, args, this); } else { manager.restartLoader(this.id, args, this); } }
(Que encontré en algún lugar de Stack-Overflow)
Una vez más, esto funcionó por un tiempo, pero todavía lanzó el fallo ocasional.
De lo que puedo averiguar durante la depuración, creo que hay algo que ver con guardar / restaurar el estado de la instancia que requiere que initLoader (/ s) se ejecutan en la parte onCreate del ciclo de vida si se quiere sobrevivir a una vuelta del ciclo . ( Puedo estar equivocado.)
En el caso de los gerentes que no se puede iniciar hasta que los resultados vuelven de otro gestor o tarea (es decir, no se puede inicializar en onCreate ), sólo uso initLoader . (Puedo no ser correcto en esto, pero parece que funciona.Estos cargadores secundarios no son parte del estado de instancia inmediata por lo que el uso de initLoader puede ser correcto en este caso)
Mirando los diagramas y documentos, habría pensado que initLoader debería ir enCreate & restartLoader en onRestart para Actividades pero eso deja Fragmentos usando algún patrón diferente y no he tenido tiempo de investigar si esto es realmente estable. ¿Puede alguien comentar si tienen éxito con este patrón de actividades?
Si el cargador ya existe, restartLoader detendrá / cancelará / destruirá el antiguo, mientras que initLoader solo lo inicializará con la devolución de llamada dada. No puedo averiguar lo que hacen los antiguos callbacks en estos casos, pero supongo que simplemente serán abandonados.
Escaneé a través de http://grepcode.com/file/repository.grepcode.com/java/ext/com.google.android/android/4.0.1_r1/android/app/LoaderManager.java pero no puedo averiguar cuál es el exacto Diferencia es, aparte de que los métodos hacen cosas diferentes. Así que yo diría, use initLoader la primera vez y reinicie para los siguientes tiempos, aunque no puedo decir con certeza lo que cada uno de ellos hará exactamente.
Init cargador en el primer inicio utiliza el método loadInBackground (), en el segundo inicio se omitirá. Por lo tanto, mi opinión, la mejor solución es:
Loader<?> loa; try { loa = getLoaderManager().getLoader(0); } catch (Exception e) { loa = null; } if (loa == null) { getLoaderManager().initLoader(0, null, this); } else { loa.forceLoad(); }
////////////////////////////////////////////////// /////////////////////////
protected SimpleCursorAdapter mAdapter; private abstract class SimpleCursorAdapterLoader extends AsyncTaskLoader <Cursor> { public SimpleCursorAdapterLoader(Context context) { super(context); } @Override protected void onStartLoading() { if (takeContentChanged() || mAdapter.isEmpty()) { forceLoad(); } } @Override protected void onStopLoading() { cancelLoad(); } @Override protected void onReset() { super.onReset(); onStopLoading(); } }
Pasé mucho tiempo para encontrar esta solución – restartLoader (…) no funcionó correctamente en mi caso. El único forceLoad () permite terminar el hilo de carga anterior sin devolución de llamada (por lo que tendrá todas las transacciones db finalizadas correctamente) y vuelve a iniciar el nuevo hilo. Sí, requiere tiempo extra, pero es más estable. Sólo el último hilo iniciado tomará devolución de llamada. Por lo tanto, si desea hacer pruebas con la interrupción de sus transacciones db – su bienvenida, trate de restartLoader (…), de lo contrario forceLoad (). La única conveniencia de restartLoader (…) es entregar nuevos datos iniciales, me refiero a los parámetros. Y por favor no olvide destruir el cargador en el método onDetach () del fragmento adecuado en este caso. Tenga en cuenta también que algunas veces, cuando tiene una actividad y, digamos, dos fragmentos con Loader cada actividad inclusiva, solo alcanzará a 2 Administradores de cargador, de modo que la actividad comparte su LoaderManager con fragmento (s), es decir Mostrado en la pantalla primero durante la carga. Pruebe LoaderManager.enableDebugLogging (true); Para ver los detalles en cada caso determinado.
initLoader
reutilizará los mismos parámetros si el cargador ya existe. Devuelve inmediatamente si los datos antiguos ya están cargados, aunque lo llame con nuevos parámetros. Lo ideal sería que el cargador notifique automáticamente la actividad de los nuevos datos. Si la pantalla gira, initLoader
se initLoader
a llamar y los datos antiguos se mostrarán de inmediato.
restartLoader
es para cuando se quiere forzar una recarga y cambiar los parámetros también. Si restartLoader
que crear una pantalla de inicio de sesión utilizando cargadores, llamaría sólo restartLoader
cada vez que se restartLoader
clic en el botón. (Es posible hacer clic en el botón varias veces debido a credenciales incorrectas, etc.). Sólo se podría llamar a initLoader
al restaurar el estado de la instancia guardada de la actividad en caso de que se initLoader
la pantalla mientras un inicio de sesión estaba en curso.
- ¿Cómo habilitar LogCat / Console en Eclipse para Android?
- Diferencia entre Fragmento y FragmentoActividad