Cursor.getType() для уровня API <11
Я запрашиваю у поставщика контента CallLog и вам нужно определить типы столбцов.
В Honeycomb и new (API Level 11+) вы можете получить предпочтительный тип столбцов, вызвав метод Cursor.getType(int columnIndex)
, который возвращает один из следующих типов:
- FIELD_TYPE_NULL (0)
- FIELD_TYPE_INTEGER (1)
- FIELD_TYPE_FLOAT (2)
- FIELD_TYPE_STRING (3)
- FIELD_TYPE_BLOB (4)
Как я могу выполнить это на устройствах с предварительной сотой и 11?
Я пробовал следующее:
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;
}
}
}
}
}
Однако исключение не выбрасывается. Данные всегда выдаются в первом типе, который вы проверяете, в этом случае getInt(). Это означает, что я получаю правильные значения, если тип столбца Integer, но 0 для всех других типов.
Почему я не ищу в документации, чтобы проверить, какой тип хранится?
Колонки различаются в зависимости от производителя устройства, и не все из них документированы, см. Этот вопрос: Как обрабатывать различия, зависящие от производителя в ContentProviders?
Любые идеи?
Ответы
Ответ 1
Вы можете использовать этот код, если курсор расположен в допустимой строке:
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";
}
}
Обратите внимание, что константы Cursor.FIELD_TYPE_ * определены начиная с HONEYCOMB.
Ответ 2
Развернувшись на ответ Хуана, вот моя замена для метода API 11 Cursor.getType(int i) - для курсора, перенастроенного 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
Ответ 3
Есть что-то, что может сработать:
http://developer.android.com/reference/android/database/DatabaseUtils.html cursorRowToContentValues
скопирует строку в объекте ContentValues. Затем вы можете вызвать ContentValues.get(), который даст вам объект. вы можете посмотреть класс этого объекта.
изменить
В соответствии с исходным кодом DatabaseUtils объектом являются либо blobs, либо строки.
изменить 2
Однако, если ваш курсор является WindowedCursor, он имеет методы для определения типов объектов. (isBlob, isString, isLong...)
Ответ 4
Я столкнулся с той же проблемой в прошлом. Я занялся этим довольно приятным решением.
Сопоставьте это с вашими потребностями.
В моем случае у меня есть множество разных объектов, которые все синхронизируются с сервером в облаке. Все они имеют общие свойства, так что все они наследуются от общего BaseObject.
Этот объект имеет метод, который принимает курсор в качестве параметра и возвращает новый объект того же типа, так что каждый объект, который наследуется от него, переопределяет этот метод с расширенными его свойствами.
* Обратите внимание, что наследование объектов не требуется для этого подхода. Это просто более разумный способ сделать это. Пока у вас есть тот же метод во всех объектах, которые вам нужны для создания БД формы, это будет работать, поскольку вы сможете увидеть в конце.
Позвольте мне проиллюстрировать это:
Наш базовый объект.
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;
}
}
Новый объект, который наследуется от первого.
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;
}
}
Наконец, в вашей базе данных вам будет проще запросить нужные данные, вызывая общий метод "getAllObjects" и передавая тип класса, который вы хотите запросить, вместе с другими параметрами запроса:
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;
}
И ты туда. Один общий метод для получения любого объекта из вашей базы данных и для успешного превращения его в объект или массив объектов во время выполнения.
Примечания:
- Каждый объект должен иметь метод getTable(), чтобы иметь возможность запрашивать правильную таблицу
- В этом подходе также есть соединение OODB, как вы можете видеть. Вы можете использовать тот же подход без этого, просто запросив все элементы (SELECT * FROM...)
Надеюсь, это поможет. Ответьте на вопросы или сомнения.