¿Cuál es la mejor manera de iterar un Cursor de Android?

Frecuentemente veo código que implica iterar sobre el resultado de una consulta en la base de datos, hacer algo con cada fila y luego pasar a la siguiente fila. Los 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()); } 

Todo esto parece excesivamente largo para mí, cada uno con múltiples llamadas a métodos de Cursor . Seguramente debe haber una manera más ordenada?

La forma más simple es esta:

 while (cursor.moveToNext()) { ... } 

El cursor comienza antes de la primera fila de resultados, por lo que en la primera iteración, esto pasa al primer resultado, si existe . Si el cursor está vacío o la última fila ya ha sido procesada, entonces el ciclo sale limpiamente.

Por supuesto, no olvide cerrar el cursor una vez que haya terminado con él, preferiblemente en una cláusula finally .

 Cursor cursor = db.rawQuery(...); try { while (cursor.moveToNext()) { ... } } finally { cursor.close(); } 

Si segmenta API 19+, puede usar try-with-resources.

 try (Cursor cursor = db.rawQuery(...)) { while (cursor.moveToNext()) { ... } } 

La mejor forma de mirar que he encontrado para pasar por un cursor es la siguiente:

 Cursor cursor; ... //fill the cursor here for (cursor.moveToFirst(); !cursor.isAfterLast(); cursor.moveToNext()) { // do what you need with the cursor here } 

No olvides cerrar el cursor luego

EDITAR: la solución dada es excelente si alguna vez necesitas iterar un cursor del que no eres responsable. 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.

Me gustaría señalar una tercera alternativa que también funciona si el cursor no está en la posición de inicio:

 if (cursor.moveToFirst()) { do { // do what you need with the cursor here } while (cursor.moveToNext()); } 

¿Qué hay de usar el 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 cierra automáticamente el cursor:

 public class CursorUtils { public static Iterable iterate(Cursor cursor) { return new IterableWithObject(cursor) { @Override public Iterator iterator() { return new IteratorWithObject(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 implements Iterator { protected T t; public IteratorWithObject(T t) { this.t = t; this.onCreate(); } protected abstract void onCreate(); } private static abstract class IterableWithObject implements Iterable { protected T t; public IterableWithObject(T t) { this.t = t; } } } 

Debajo 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 atravesaría toda la iteración y no escaparía a la primera y a la última iteración.

 import java.util.Iterator; import android.database.Cursor; public class IterableCursor implements Iterable, Iterator { Cursor cursor; int toVisit; public IterableCursor(Cursor cursor) { this.cursor = cursor; toVisit = cursor.getCount(); } public Iterator 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(); } } 

Código de ejemplo:

 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(); } 

La solución Do / While es más elegante, pero si solo utiliza la solución While publicada anteriormente, sin moveToPosition (-1) perderá el primer elemento (al menos en la consulta de contacto).

Yo sugiero:

 if (cursor.getCount() > 0) { cursor.moveToPosition(-1); while (cursor.moveToNext()) {  } } 
 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 mostrar usando moveToNext() puede iterar el cursor cuando la grabación no existe, entonces return false , a menos que return true ,

 while (cursor.moveToNext()) { ... } 

Antes de saber cómo iterar sobre un Cursor, debes saber qué es un Cursor en general o en Android.
Cursor en Android es una interfaz que representa un conjunto de resultados devuelto por una base de datos

(En términos sencillos, un cursor es una tabla con filas y columnas).

La interfaz del cursor tiene métodos bastante útiles para jugar. Por ejemplo, obtener números totales de filas y columnas. Puede consultar el nombre de la columna, incluso puede consultar los valores almacenados en una fila en particular. Una vez que sepas que iterar sobre un cursor es solo otra cosa de progtwigción que habrías hecho en el pasado.
Aquí es cómo generalmente cómo iterar sobre un cursor.
Obtenga el número total de filas usando la función getCount () de la interfaz del cursor

  int rowSize = cursor.getCount(); cursor.moveToFirst(); // resets the value to -1 for(int i=0;i