initloader - loaders en android
¿Cuáles son los beneficios de CursorLoaders? (2)
Hay dos ventajas clave para usar un CursorLoader
en su aplicación sobre Activity.managedQuery()
:
- La consulta se maneja en un subproceso en segundo plano para usted (cortesía de la compilación en
AsyncTaskLoader
) para que las consultas de datos grandes no bloqueen la interfaz de usuario. Esto es algo que los documentos recomiendan que hagas por ti mismo cuando usas unCursor
plano, pero ahora está hecho bajo el capó. -
CursorLoader
se actualiza automáticamente. Además de realizar la consulta inicial, elCursorLoader
registra unContentObserver
con el conjunto de datos que solicitó y llama aforceLoad()
sobre sí mismo cuando cambia el conjunto de datos. Esto da como resultado que obtenga devoluciones de llamada asíncronas en cualquier momento en que los datos cambien para actualizar la vista.
Cada instancia de Loader
también se maneja a través del LoaderManager
singular, por lo que aún no tiene que administrar el cursor directamente, y ahora la conexión puede persistir incluso más allá de una sola Activity
. LoaderManager.initLoader()
y LoaderManager.restartLoader()
permiten reconectarse con un Loader
existente ya configurado para su consulta y, en algunos casos, obtener instantáneamente los datos más recientes, si están disponibles.
Su Activity
o Fragment
probablemente ahora implementará la interfaz LoaderManager.Callback
. Al llamar a initLoader()
se onCreateLoader()
método onCreateLoader()
donde construirá la consulta y una nueva instancia de CursorLoader
, si es necesario. El método onLoadFinished()
se activará cada vez que haya nuevos datos disponibles, e incluirá el Cursor
más reciente para que se adjunte a la vista o se realice una iteración.
Además, hay un buen ejemplo de todo este ajuste en la página de documentación de la clase LoaderManager
: http://developer.android.com/reference/android/app/LoaderManager.html
¡Espero que ayude!
Utilizo los Cursors
ampliamente en mi aplicación para cargar y ocasionalmente escribir información desde y hacia una base de datos. He visto que Honeycomb y el paquete de compatibilidad tienen nuevas clases de Loader
diseñadas para ayudar a cargar datos de una manera "buena".
Básicamente, ¿son estas nuevas clases (en particular CursorLoader
) considerablemente mejores que los métodos anteriores de administración de datos? ¿Cuál es el beneficio de un CursorLoader
sobre los Cursors
administrados, por ejemplo?
Y utilizo un ContentProvider
para tratar con los datos, que obviamente toma Uris
pero ¿cómo encaja esto con el método initLoader()
? ¿Debo configurar cada uno de mis Fragments
para usar los Cargadores individualmente? ¿Y qué tan único debe ser el ID para cada cargador, está sobre el alcance de mi aplicación o solo un fragmento? ¿Hay alguna forma sencilla de pasar un Uri
a un CursorLoader para consultar mis datos?
Todo lo que puedo ver en este momento es que los Cargadores agregan un paso adicional innecesario para incluir mis datos en mi aplicación, ¿puede alguien explicármelos mejor?
Si alguien se encuentra en una situación similar, esto es lo que he hecho:
- Creó una clase que implementa
LoaderCallbacks
y maneja todas las consultas que necesitará. - Suministre esto con un
Context
y elAdapter
en cuestión. - Cree identificadores únicos para cada consulta que usará (si usa un
UriMatcher
, también podría usar los mismos) - Haga un método conveniente que transfiera las consultas al paquete requerido para las
LoaderCallbacks
- Eso es más o menos :) Pongo parte de mi código a continuación para mostrar exactamente lo que hice
En mi clase de GlobalCallbacks
:
public static final String PROJECTION = "projection";
public static final String SELECTION = "select";
public static final String SELECTARGS = "sargs";
public static final String SORT = "sort";
Context mContext;
SimpleCursorAdapter mAdapter;
public GlobalCallbacks(Context context, SimpleCursorAdapter adapter) {
mContext = context;
mAdapter = adapter;
}
@Override
public Loader<Cursor> onCreateLoader(int id, Bundle args) {
Uri contentUri = AbsProvider.customIntMatch(id);
if (contentUri != null) {
return new CursorLoader(mContext, contentUri, args.getStringArray(PROJECTION), args.getString(SELECTION),
args.getStringArray(SELECTARGS), args.getString(SORT));
} else return null;
}
@Override
public void onLoadFinished(Loader<Cursor> arg0, Cursor arg1) {
mAdapter.swapCursor(arg1);
}
@Override
public void onLoaderReset(Loader<Cursor> arg0) {
mAdapter.swapCursor(null);
}
Y cuando quise usar un CursorLoader
( Helper.bundleArgs()
es el método de agrupación conveniente):
scAdapt = new Adapters.NewIndexedAdapter(mHost, getMenuType(),
null, new String[] { "name" }, new int[] { android.R.id.text1 });
getLoaderManager().initLoader(
GlobalCallbacks.GROUP,
Helper.bundleArgs(new String[] { "_id", "name" }),
new GlobalCallbacks(mHost, scAdapt));
setListAdapter(scAdapt);
Y en ayudante:
public static Bundle bundleArgs(String[] projection, String selection, String[] selectionArgs) {
Bundle b = new Bundle();
b.putStringArray(GlobalCallbacks.PROJECTION, projection);
b.putString(GlobalCallbacks.SELECTION, selection);
b.putStringArray(GlobalCallbacks.SELECTARGS, selectionArgs);
return b;
}
Espero que esto ayude a alguien más :)
EDITAR
Para explicar más a fondo:
- Primero, se inicializa un adaptador con un
Cursor
nulo. No lo suministramos con unCursor
porqueGlobalCallbacks
le dará al adaptador elCursor
correcto enonLoadFinished(..)
- A continuación, le decimos a
LoaderManager
que queremos inicializar un nuevoCursorLoader
. Proporcionamos una nueva instancia deGlobalCallbacks
(que implementaLoader.Callbacks
) que luego monitoreará la carga del cursor. También tenemos que suministrarlo con el adaptador, para que pueda intercambiar el nuevoCursor
una vez que se haya cargado. En algún momento, elLoaderManager
(que está integrado en el sistema operativo) invocaráonCreateLoader(..)
deGlobalCallbacks
y comenzará a cargar datos de forma asíncrona. -
Helper.bundleArgs(..)
simplemente coloca los argumentos para la consulta en unBundle
(por ejemplo, proyección de columnas, orden de clasificación, cláusula WHERE) - Luego configuramos el
ListAdapter
lista delFragment
. El cursor seguirá siendo nulo en este punto, por lo que mostrará un signo de carga o un mensaje vacío hasta que seonLoadFinished()