¿Cuál es la mejor manera de iterar un Cursor Android?
Con frecuencia veo código que implica iterar sobre el resultado de una consulta de base de datos, hacer algo con cada fila y, a continuación, pasar a la siguiente fila. Ejemplos típicos son los siguientes.
Cursor cursor = db.rawQuery(...); cursor.moveToFirst(); while (cursor.isAfterLast() == false) { ... cursor.moveToNext(); }
Cursor cursor = db.rawQuery(...); for (boolean hasItem = cursor.moveToFirst(); hasItem; hasItem = cursor.moveToNext()) { ... }
Cursor cursor = db.rawQuery(...); if (cursor.moveToFirst()) { do { ... } while (cursor.moveToNext()); }
Todos ellos me parecen excesivamente largos, cada uno con múltiples llamadas a los métodos Cursor
. Seguramente debe haber una manera más limpia?
- Android Cursor.registerContentObserver ()
- Notificar cambios de datos en un cursor sin utilizar un ContentProvider
- Escribir dentro de un cursor Android
- Utilizar MergeCursor
- Agregar el contenido de un Cursor a otro Cursor
- Finalizando un Cursor que no ha sido desactivado o cerrado error no fatal
- ¿Cómo obtener la cadena del elemento seleccionado de SimpleCursorAdapter?
- POJO versus Cursores en Android
- Modificar el valor de las columnas Cursor en android
- SQLite CursorWindowAllocationException crash
- Límite de tamaño del cursor en Android SQLiteDatabase
- Cursor de Android en idiomas RTL
- Consulta getContentResolver causa la advertencia CursorWrapperInner
La forma más sencilla es esta:
while (cursor.moveToNext()) { ... }
El cursor comienza antes de la primera fila de resultados, por lo que en la primera iteración se mueve al primer resultado si existe . Si el cursor está vacío o la última fila ya ha sido procesada, el bucle sale correctamente.
Por supuesto, no olvide cerrar el cursor una vez que haya terminado con él, preferiblemente en una cláusula final.
Cursor cursor = db.rawQuery(...); try { while (cursor.moveToNext()) { ... } } finally { cursor.close(); }
Si apunta a la API 19+, puede utilizar try-with-resources.
try (Cursor cursor = db.rawQuery(...)) { while (cursor.moveToNext()) { ... } }
La mejor manera de mirar que he encontrado para ir a través de un cursor es el siguiente:
Cursor cursor; ... //fill the cursor here for (cursor.moveToFirst(); !cursor.isAfterLast(); cursor.moveToNext()) { // do what you need with the cursor here }
No olvide cerrar el cursor después
EDIT: La solución dada es grande si alguna vez necesita para iterar un cursor que no son responsables. Un buen ejemplo sería, si está tomando un cursor como argumento en un método, y necesita escanear el cursor para un valor dado, sin tener que preocuparse por la posición actual del cursor.
Sólo me gustaría señalar una tercera alternativa que también funciona si el cursor no está en la posición inicial:
if (cursor.moveToFirst()) { do { // do what you need with the cursor here } while (cursor.moveToNext()); }
¿Cómo sobre el uso de bucle foreach:
Cursor cursor; for (Cursor c : CursorUtils.iterate(cursor)) { //c.doSth() }
Sin embargo, mi versión de CursorUtils debería ser menos fea, pero automáticamente cierra el cursor:
public class CursorUtils { public static Iterable<Cursor> iterate(Cursor cursor) { return new IterableWithObject<Cursor>(cursor) { @Override public Iterator<Cursor> iterator() { return new IteratorWithObject<Cursor>(t) { @Override public boolean hasNext() { t.moveToNext(); if (t.isAfterLast()) { t.close(); return false; } return true; } @Override public Cursor next() { return t; } @Override public void remove() { throw new UnsupportedOperationException("CursorUtils : remove : "); } @Override protected void onCreate() { t.moveToPosition(-1); } }; } }; } private static abstract class IteratorWithObject<T> implements Iterator<T> { protected T t; public IteratorWithObject(T t) { this.t = t; this.onCreate(); } protected abstract void onCreate(); } private static abstract class IterableWithObject<T> implements Iterable<T> { protected T t; public IterableWithObject(T t) { this.t = t; } } }
Abajo podría ser la mejor manera:
if (cursor.moveToFirst()) { while (!cursor.isAfterLast()) { //your code to implement cursor.moveToNext(); } } cursor.close();
El código anterior aseguraría que pasaría por iteración completa y no escapará la primera y última iteración.
La solución Do / While es más elegante, pero si utiliza sólo la solución While que se ha publicado anteriormente, sin moveToPosition (-1), se perderá el primer elemento (al menos en la consulta de contacto).
Yo sugiero:
if (cursor.getCount() > 0) { cursor.moveToPosition(-1); while (cursor.moveToNext()) { <do stuff> } }
import java.util.Iterator; import android.database.Cursor; public class IterableCursor implements Iterable<Cursor>, Iterator<Cursor> { Cursor cursor; int toVisit; public IterableCursor(Cursor cursor) { this.cursor = cursor; toVisit = cursor.getCount(); } public Iterator<Cursor> iterator() { cursor.moveToPosition(-1); return this; } public boolean hasNext() { return toVisit>0; } public Cursor next() { // if (!hasNext()) { // throw new NoSuchElementException(); // } cursor.moveToNext(); toVisit--; return cursor; } public void remove() { throw new UnsupportedOperationException(); } }
Ejemplo de código:
static void listAllPhones(Context context) { Cursor phones = context.getContentResolver().query(ContactsContract.CommonDataKinds.Phone.CONTENT_URI, null, null, null, null); for (Cursor phone : new IterableCursor(phones)) { String name = phone.getString(phone.getColumnIndex(ContactsContract.CommonDataKinds.Phone.DISPLAY_NAME)); String phoneNumber = phone.getString(phone.getColumnIndex(ContactsContract.CommonDataKinds.Phone.NUMBER)); Log.d("name=" + name + " phoneNumber=" + phoneNumber); } phones.close(); }
if (cursor.getCount() == 0) return; cursor.moveToFirst(); while (!cursor.isAfterLast()) { // do something cursor.moveToNext(); } cursor.close();
Inicialmente el cursor no está en la primera fila usando moveToNext()
puede iterar el cursor cuando el registro no existe, entonces return false
, a menos que return true
,
while (cursor.moveToNext()) { ... }
- ¿Cómo mostrar un cuadro de diálogo Sí / No en Android?
- ¿Cómo se muestra el valor actual de una preferencia de Android en el resumen de preferencias?