Pregunta

Estoy consultando al proveedor de contenido CallLog y necesito detectar los tipos de columnas.

En Honeycomb y versiones posteriores (API nivel 11+), puede obtener el tipo de datos preferido de una columna llamando al método Cursor.getType(int columnIndex) que devuelve uno de los siguientes tipos:

  • FIELD_TYPE_NULL (0)
  • FIELD_TYPE_INTEGER (1)
  • FIELD_TYPE_FLOAT (2)
  • FIELD_TYPE_STRING (3)
  • FIELD_TYPE_BLOB (4)

¿Cómo puedo lograr esto en dispositivos anteriores a Honeycomb <11?

Intenté lo siguiente:

for ( int i = 0; i < cursor.getColumnCount(); i++ ) {    

    int columnType = -1;
    try {
        cursor.getInt( i );
        columnType = Cursor.FIELD_TYPE_INTEGER;

    } catch ( Exception ignore ) {

        try {
            cursor.getString( i );
            columnType = Cursor.FIELD_TYPE_STRING;

        } catch ( Exception ignore1 ) {

            try {
                cursor.getFloat( i );
                columnType = Cursor.FIELD_TYPE_FLOAT;

            } catch ( Exception ignore2 ) {

                try {                                             
                  cursor.getBlob( i );
                  columnType = Cursor.FIELD_TYPE_BLOB;

                } catch ( Exception ignore3 ) {

                     columnType = Cursor.FIELD_TYPE_NULL;
                }
           }
       }
   }

}

Sin embargo, no se lanza ninguna excepción.Los datos siempre se convierten en el primer tipo que está comprobando, en este caso getInt().Eso significa que obtengo los valores correctos si el tipo de columna es Entero pero un 0 para todos los demás tipos.

¿Por qué no busco en la documentación para comprobar qué tipo está almacenado?Las columnas difieren según el fabricante del dispositivo y no todas están documentadas; consulte esta pregunta: ¿Cómo manejar las diferencias que dependen del fabricante en ContentProviders?

¿Algunas ideas?

¿Fue útil?

Solución

Puede utilizar este código cuando el cursor esté colocado en una fila válida:

CursorWrapper cw = (CursorWrapper)cursor;

Class<?> cursorWrapper = CursorWrapper.class;
Field mCursor = cursorWrapper.getDeclaredField("mCursor");
mCursor.setAccessible(true);
AbstractWindowedCursor abstractWindowedCursor = (AbstractWindowedCursor)mCursor.get(cw);
CursorWindow cursorWindow = abstractWindowedCursor.getWindow();
int pos = abstractWindowedCursor.getPosition();
for ( int i = 0; i < cursor.getColumnCount(); i++ ) {
    String type = null;
    if (cursorWindow.isNull(pos, i)) {
        type = "Cursor.FIELD_TYPE_NULL";
    } else if (cursorWindow.isLong(pos, i)) {
        type = "Cursor.FIELD_TYPE_INTEGER";
    } else if (cursorWindow.isFloat(pos, i)) {
        type = "Cursor.FIELD_TYPE_FLOAT";
    } else if (cursorWindow.isString(pos, i)) {
        type = "Cursor.FIELD_TYPE_STRING";
    } else if (cursorWindow.isBlob(pos, i)) {
        type = "Cursor.FIELD_TYPE_BLOB";
    }
}

Tenga en cuenta que los valores constantes de Cursor.FIELD_TYPE_* se definen a partir de HONEYCOMB.

Otros consejos

Ampliando la respuesta de Juan, aquí está mi reemplazo para el método API 11 Cursor.getType(int i) - para un cursor reajustado por una consulta SQL

public class DbCompat {

    protected static final int FIELD_TYPE_BLOB = 4;
    protected static final int FIELD_TYPE_FLOAT = 2;
    protected static final int FIELD_TYPE_INTEGER = 1;
    protected static final int FIELD_TYPE_NULL = 0;
    protected static final int FIELD_TYPE_STRING = 3;

    static int getType(Cursor cursor, int i) throws Exception {
        SQLiteCursor sqLiteCursor = (SQLiteCursor) cursor;
        CursorWindow cursorWindow = sqLiteCursor.getWindow();
        int pos = cursor.getPosition();
        int type = -1;
        if (cursorWindow.isNull(pos, i)) {
            type = FIELD_TYPE_NULL;
        } else if (cursorWindow.isLong(pos, i)) {
            type = FIELD_TYPE_INTEGER;
        } else if (cursorWindow.isFloat(pos, i)) {
            type = FIELD_TYPE_FLOAT;
        } else if (cursorWindow.isString(pos, i)) {
            type = FIELD_TYPE_STRING;
        } else if (cursorWindow.isBlob(pos, i)) {
            type = FIELD_TYPE_BLOB;
        }

        return type;
    }
}

esencia: https://gist.github.com/kassim/c340cbfc5243db3a4826

Hay algo que puede funcionar:http://developer.android.com/reference/android/database/DatabaseUtils.html cursorRowToContentValues

Copiará la fila en un objeto ContentValues.Luego, puedes llamar a ContentValues.get(), que te proporciona un objeto.Luego puedes mirar la clase de este objeto.

editar

Según el código fuente de DatabaseUtils, los objetos son blobs o cadenas.

editar 2

Sin embargo, si su cursor es un WindowedCursor, tiene métodos para conocer los tipos de objetos.(isBlob, isString, isLong...)

Me enfrenté al mismo problema en el pasado.Lo abordé con una solución bastante buena.Haga coincidir esto con sus necesidades.En mi caso, tengo una cantidad de objetos diferentes que están todos sincronizados con un servidor en la nube.Todos tienen propiedades comunes, por lo que heredan de un objeto base común.Este objeto tiene un método que toma un cursor como parámetro y devuelve un nuevo objeto del mismo tipo, de modo que cada objeto que hereda de él, anula este método con las propiedades extendidas del mismo.

*Tenga en cuenta que la herencia de objetos no es necesaria para este enfoque.Es simplemente una forma más inteligente de hacerlo.Siempre que tenga el mismo método en todos los objetos que necesita para tomar la base de datos, esto funcionará, como podrá ver al final.

Permítanme ilustrar eso:

Nuestro objeto base.

public class BaseObject{

    protected int number;
    protected String text;

    public <T extends BaseObject> T setObject(Cursor c) {
        number = c.getInt(cur.getColumnIndexOrThrow(COLUMN_NAME_FOR_NUMBER));
        text = c.getString(cur.getColumnIndexOrThrow(COLUMN_NAME_FOR_TEXT));

        return (T) this;
    }
}

Un nuevo objeto que hereda del primero.

public class Contact extends BaseObject{

    private String name;

    @Override
    public <T extends BaseObject> T setObject(Cursor c) {

        super.setObject(c);

        name = c.getString(cur.getColumnIndexOrThrow(COLUMN_NAME_FOR_NAME));

        return (T) this;
    }
}

Finalmente en tu base de datos es tan fácil como pedir los datos que deseas llamando a un método genérico "getAllObjects" y pasando el tipo de clase que deseas consultar junto con los demás parámetros de la consulta:

public synchronized <T extends BaseObject> ArrayList<T> getObjectsForClass(final Class<T> classType,
        String selection, String[] selectionArgs, String sort, String limit) {

    ArrayList<T> objects = null;

    if (db == null || !db.isOpen()) {
        db = getWritableDatabase();
    }

    objects = new ArrayList<T>();

    Cursor c = null;
    T object;
    try {
        object = classType.newInstance();

        String table = object.getTable();

        StringBuilder tableSb = new StringBuilder();
        tableSb.append(table).append(" INNER JOIN ").append(Constants.DB_BASE_OBJECT_TABLE)
                .append(" ON ").append(table).append(".").append(BaseObject.DB_OBJECT_ID_KEY).append(" = ")
                .append(Constants.DB_BASE_OBJECT_TABLE).append(".")
                .append(BaseObject.DB_ID_KEY);

        c = db.query(tableSb.toString(), null, selection, selectionArgs, null, null, sort, limit);

        if (c.getCount() > 0) {
            c.moveToFirst();
            while (!c.isAfterLast()) {

                object = classType.newInstance();
                object.setObject(c);
                objects.add(object);

                c.moveToNext();
            }
        }

    } catch (InstantiationException e) {
        e.printStackTrace();
    } catch (IllegalAccessException e) {
        e.printStackTrace();
    }
    c.close();

    return objects;
}

Y ahí lo tienes.Un método genérico para obtener cualquier objeto de su base de datos y convertirlo exitosamente en un objeto o una matriz de objetos en tiempo de ejecución.

Notas:

  • Cada objeto debe tener un método getTable() para poder consultar la tabla correcta
  • En este enfoque también hay una conexión OODB, como puede ver.Puede utilizar el mismo enfoque sin eso, simplemente consultando todos los elementos (SELECCIONAR * DESDE...)

Espero eso ayude.Responda con problemas o dudas.

Licenciado bajo: CC-BY-SA con atribución
No afiliado a StackOverflow
scroll top