- Pueden ser usados desde las actividades y fragmentos de nuestra aplicación.
- La carga de los datos puede realizarse de manera asíncrona.
- Detectan cambios en la fuente de datos y entregan los nuevos resultados.
- Si la configuración del dispositivo cambia, el cargador recuperará los datos de la última consulta sin tener que volver a repetirla, con el consiguiente ahorro de tráfico y procesamiento de datos.
Un resumen de la API.
LoaderManager
. Clase abstracta a la que podremos acceder desde una actividad o fragmento y que se encargará de gestionar las instancias de los cargadores existentes derivados de la claseLoader
.LoaderManager.LoaderCallbacks
. Interfaz utilizada por el gestor de cargadoresLoaderManager
. Por ejemplo, para crear un nuevo cargador utilizaremos el métodoonCreateLoader()
.Loader
. Clase abstracta responsable de la carga asíncrona de datos. Se trata de la clase base de un cargador. Normalmente usaremos la clase derivadaCursorLoader
, aunque podríamos, si fuera necesario, hacer nuestra propia implementación. Mientras un cargador está activo, éste controlará la fuente de datos ante posibles cambios y si éstos se producen, se encargará de entregarnos los nuevos resultados.AsyncTaskLoader
. Cargador abstracto que cuenta con una tarea asíncronaAsynkTask
para hacer el trabajo.CursorLoader
. Una subclase deAsyncTaskLoader
que hace consultas a través deContentResolver
y nos devolverá unCursor
. Esta clase implementa la clase abstractaLoader
y usa unAsyncTaskLoader
para la carga asíncrona de datos desde unContentProvider
. Al utilizar su propia hebra de ejecución, la carga de datos no afectará a la hebra de la IU.
Estos son los elementos básicos a tener en cuenta a la hora de usar cargadores en nuestras aplicaciones. De ellos, al menos, tendremos que hacer uso del
LoaderManager
para iniciar nuestro cargador y de una implementación de la clase Loader
como puede ser la clase CursorLoader
.Usando los cargadores en una aplicación.
Para usar un cargador, necesitaremos los siguientes requisitos:
- Una actividad o fragmento.
- La instancia del gestor de cargadores
LoaderManager
. - Un
CursorLoader
para la carga de contenidos asociados a unContentProvider
. También podemos implementar nuestro propioLoader
oAsyncTaskLoader
para cargar los datos desde una fuente de naturaleza distinta. - Una implementación de la interfaz
LoaderManager.LoaderCallbacks
. Es desde donde vamos a crear nuevos cargadores y gestionar las referencias a los ya existentes. - Una manera de visualizar los datos cargados, como el adaptador
SimpleCursorAdapter
. - Una fuente de datos de tipo
ContentProvider
complementaria a nuestro cargador de tipoCursorLoader
.
Iniciando el cargador.
El gestor de cargadores
Normalmente, iniciaremos nuestros cargadores desde el método
El método LoaderManager
puede gestionar varias instancias de cargadores Loader
desde una actividad o fragmento. Solo hay una instancia del gestor de cargadores (patrón Sigleton).Normalmente, iniciaremos nuestros cargadores desde el método
onCreate()
de una actividad o desde el método onActivityCreated()
de un fragmento: // Tanto si existe como si no, siempre tendremos que iniciar el cargador. getLoaderManager().initLoader(0, null, this);
initLoader()
cuenta con los siguientes parámetros: - Un
ID
único que identifica al cargador. En el ejemplo, elID
vale0
. - Argumentos opcionales que utilizaremos construir el nuestro cargador. En el ejemplo, los argumentos valen
null
. - Una implementación de la interfaz
LoaderManager.LoaderCallbacks
que elLoaderManager
utilizará para gestionar la carga de datos. En el ejemplo, la interfaz es implementada por el objetothis
(nuestra actividad o fragmento).
El método
initLoader()
se asegura de que el cargador está iniciado y activo. Cuando invocamos este método, pueden suceder dos cosas:- Si el
ID
del cargador especificado ya existe, éste será reutilizado. - Si el
ID
del cargador especificado no existe,initLoader()
ejecuta el métodoonCreateLoader()
de su interfazLoaderManager.LoaderCallbacks
. En este método implementaremos lo necesario para instanciar y devolver el nuevo cargador.
LoaderManager.LoaderCallbacks
asociada al cargador se utilizará cada vez que el cargador cambie su estado. Ten en cuenta, que si a la hora de llamar al método initLoader()
el cargador ya disponía de datos de alguna consulta anterior, se llamará directamente al método onLoadFinished()
.Aunque el método
initLoader()
devuelve el cargador recién creado, no tienes por qué almacenar su instancia en una variable. El LoaderManager
se encargará de gestionar el cargador por nosotros: se encargará de iniciar o parar la carga de datos cuando sea necesario y de mantener su estado y contenido. Es por ello por lo que no tendremos que usar los cargadores directamente y en su lugar implementaremos su interfaz LoaderManager.LoaderCallbacks
.Reiniciando el cargador.
A veces, nos interesará descartar los datos antiguos y forzar una nueva carga. Para volver a cargar los datos usaremos el método
restartLoader()
. En el siguiente ejemplo, cargamos los datos cada vez que el usuario cambia el criterio de búsqueda SearchView.OnQueryTextListener
: public boolean onQueryTextChanged(String newText) { // Llamado cada vez que el texto de búsqueda de la barra de acción cambia. // Se actualiza el filtro y se actualizan los datos del cargador teniendo // en cuenta el nuevo filtro. mCurFilter = !TextUtils.isEmpty(newText) ? newText : null; getLoaderManager().restartLoader(0, null, this); return true; }
La interfaz LoaderManager.LoaderCallbacks.
Los cargadores, en particularCursorLoader
, guardan una copia de los datos una vez ha finalizado su carga. Es decir, que podremos conservar los datos a lo largo del ciclo de vida de nuestras actividades y fragmentos.Implementar la interfaz
LoaderManager.LoaderCallbacks
nos permitirá interactuar con el LoaderManager
. La interfaz incluye los siguientes métodos:
onCreateLoader()
. Crea un nuevo cargador Loader y le asocia el ID especificado.onLoadFinished()
. Se invoca cada vez que la carga de datos finaliza.onLoaderReset()
. Se invoca cada vez que se reinicia el cargador, haciendo que sus datos ya no estén disponibles.
onCreateLoader()
Cuando accedemos al cargador a través del método
LoaderManager.initLoader()
, el gestor de cargadores comprueba la existencia de su argumento ID
. Si el ID
especificado no existe, llama al método LoaderCallbacks.onCreateLoader()
que será el encargado de crear la nueva instancia para nuestro cargador.
En el siguiente ejemplo, el método
onCreateLoader()
, crea un CursorLoader
. Para crear un CursorLoader
, tendremos que especificar la información necesaria para realizar consultas sobre un ContentProvider
. Para ello, necesitaremos:Uri
. Enlace al contenido.projection
. Cláusula de proyección. Si especificamos valornull
, se devolverán todas las columnas.selection
. Cláusula de selección.selectionArgs
. Argumentos de la cláusula de selección. Evita ataques por inyección SQL.sortOrder
. Cláusula de ordenación. Si especificamos valornull
, se utilizará el orden natural de almacenamiento.
... import android.provider.ContactsContract.Contacts; ... // Cadena de texto introducida por el usuario String mCurFilter; // Definimos la cláusula de proyección. static final String[] CONTACTS_SUMMARY_PROJECTION = new String[] { Contacts._ID, Contacts.DISPLAY_NAME, Contacts.CONTACT_STATUS, Contacts.CONTACT_PRESENCE, Contacts.PHOTO_ID, Contacts.LOOKUP_KEY, }; ... // Es llamado si el cargador no existe. public LoaderonCreateLoader(int id, Bundle args) { // Construimos la URI base a partir del filtro actual mCurFilter. Uri baseUri; if (mCurFilter != null) { baseUri = Uri.withAppendedPath(Contacts.CONTENT_FILTER_URI, Uri.encode(mCurFilter)); } else { baseUri = Contacts.CONTENT_URI; } // Definimos la cláusula de selección. String select = "((" + Contacts.DISPLAY_NAME + " NOTNULL) AND (" + Contacts.HAS_PHONE_NUMBER + "=1) AND (" + Contacts.DISPLAY_NAME + " != '' ))"; // Creamos el cargador de tipo CursorLoader y lo devolvemos. return new CursorLoader(getActivity(), baseUri, CONTACTS_SUMMARY_PROJECTION, select, null, Contacts.DISPLAY_NAME + " COLLATE LOCALIZED ASC"); }
onLoadFinished()
Este método será invocado cada vez que el cargador finalice la carga de datos. El método se invoca antes de que los datos de la última carga sean eliminados. En el ejemplo, utilizamos un adaptador (
SimpleCursorAdapter
) para mostrar los datos cargados en el cursor (CursorLoader
):// Definimos el adaptador para mostrar los datos. SimpleCursorAdapter mAdapter; ... public void onLoadFinished(Loader<Cursor> loader, Cursor data) { // Cambiamos los datos del adaptador por los nuevos (visualizamos el resultado). // El cursor con la carga anterior será cerrado automáticamente y // no tendremos que preocuparnos por ello. mAdapter.swapCursor(data); }
onLoadReset()
Método que será invocado cuando reiniciamos el cargador haciendo que sus datos ya no estén disponibles. Esto nos permitirá controlar cuándo son eliminados los datos y actuar en consecuencia:
// Definimos el adaptador para mostrar los datos. SimpleCursorAdapter mAdapter; ... public void onLoaderReset(Loaderloader) { // Se eliminan los datos del cargador y mostramos la lista vacía. mAdapter.swapCursor(null); }
No hay comentarios:
Publicar un comentario