studio sobre programacion para herramientas desarrollo con avanzado aplicaciones android android-contentprovider android-cursor

android - sobre - Cursor.getType() para el nivel de API<11



manual de programacion android (4)

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

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

Estoy consultando el proveedor de contenido de CallLog y necesito detectar los tipos de columna.

En Honeycomb y más recientes (nivel API 11+) puede obtener un tipo de datos preferido de columnas 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 los dispositivos pre-Honeycomb <11?

He intentado 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á verificando, en este caso getInt (). Eso significa que obtengo los valores correctos si el tipo de columna es Integer pero un 0 para todos los demás tipos.

¿Por qué no estoy buscando 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 dependientes del fabricante en ContentProviders?

¿Algunas ideas?


Enfrenté el mismo problema en el pasado. Lo abordé con una muy buena solución. Combina esto con tus necesidades. En mi caso, tengo una cantidad de objetos diferentes que están sincronizados con un servidor en la nube. Todos tienen propiedades comunes, de modo que todos heredan de un BaseObject 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, reemplaza este método con sus propiedades extendidas.

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

Déjame 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 su base de datos es tan fácil como solicitar los datos que desea llamando a un método genérico "getAllObjects" y pasando el tipo de clase que desea consultar junto con los otros 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í 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 usar el mismo enfoque sin eso, simplemente consultando todos los elementos (SELECCIONAR * DE ...)

Espero eso ayude. Responde con cuestiones o dudas.


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 da un objeto. entonces puedes mirar la clase de este objeto.

editar

De acuerdo con el código fuente de DatabaseUtils, el objeto es blobs o Strings.

editar 2

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


Puede usar este código cuando el cursor está posicionado 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.