para - recorrer cursor android
¿Cuál es la mejor manera de iterar un cursor de Android? (9)
¿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 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;
}
}
}
Con frecuencia veo el código que implica iterar sobre el resultado de una consulta de 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());
}
Todos estos me parecen excesivamente largos, cada uno con múltiples llamadas a los métodos del Cursor
. Seguramente debe haber una manera más ordenada?
A continuación 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 una iteración completa y no escapará a la primera y última iteración.
Inicialmente, el cursor no se encuentra en la primera fila. Usando moveToNext()
puede iterar el cursor cuando el registro no existe, luego return false
, a menos que return true
,
while (cursor.moveToNext()) {
...
}
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 se ha procesado, entonces el bucle sale perfectamente.
Por supuesto, no olvides cerrar el cursor una vez que hayas terminado con él, preferiblemente en una cláusula final.
Cursor cursor = db.rawQuery(...);
try {
while (cursor.moveToNext()) {
...
}
} finally {
cursor.close();
}
Si apuntas a API 19+, puedes 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 después.
EDITAR: La solución dada es excelente si alguna vez necesita iterar un cursor del que no es responsable. Un buen ejemplo sería, si está tomando un cursor como argumento en un método, y necesita escanear el cursor en busca de un valor determinado, sin tener que preocuparse por la posición actual del cursor.
La solución Do / While es más elegante, pero si solo usa 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()) {
<do stuff>
}
}
Solo 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());
}
if (cursor.getCount() == 0)
return;
cursor.moveToFirst();
while (!cursor.isAfterLast())
{
// do something
cursor.moveToNext();
}
cursor.close();
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();
}
}
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();
}