Вопрос

Я запрашиваю поставщика контента CallLog и мне нужно определить типы столбцов.

В Honeycomb и более поздних версиях (уровень API 11+) вы можете получить предпочтительный тип данных столбцов, вызвав метод Cursor.getType(int columnIndex) который возвращает один из следующих типов:

  • ПОЛЕ_ТИП_NULL (0)
  • ПОЛЕ_ТИП_INTEGER (1)
  • FIELD_TYPE_FLOAT (2)
  • FIELD_TYPE_STRING (3)
  • ПОЛЕ_ТИП_BLOB (4)

Как я могу это сделать на устройствах до Honeycomb <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().Это означает, что я получаю правильные значения, если тип столбца Целое число но 0 для всех остальных типов.

Почему я не просматриваю документацию, чтобы проверить, какой тип хранится?Столбцы различаются в зависимости от производителя устройства и не все из них документированы, см. этот вопрос: Как обрабатывать различия в ContentProviders, зависящие от производителя?

Есть идеи?

Это было полезно?

Решение

Вы можете использовать этот код, когда курсор расположен в допустимой строке:

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.

Другие советы

Продолжая ответ Хуана, вот моя замена метода 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;
    }
}

суть: https://gist.github.com/kassim/c340cbfc5243db3a4826

Есть кое-что, что может сработать:http://developer.android.com/reference/android/database/DatabaseUtils.html курсорРовтоконтентвалуес

скопирует строку в объект ContentValues.Затем вы можете вызвать ContentValues.get(), который предоставит вам объект.затем вы можете посмотреть класс этого объекта.

редактировать

Согласно исходному коду DatabaseUtils, объектом являются либо BLOB-объекты, либо строки.

редактировать 2

Однако, если ваш курсор является WindowedCursor, у него есть методы для определения типов объектов.(isBlob, isString, isLong...)

Я столкнулся с той же проблемой в прошлом.Я решил эту проблему с помощью довольно хорошего решения.Сопоставьте это со своими потребностями.В моем случае у меня есть несколько различных объектов, которые синхронизируются с сервером в облаке.Все они имеют общие свойства, поэтому все они наследуются от общего 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...)

Надеюсь, поможет.Ответьте на вопросы или сомнения.

Лицензировано под: CC-BY-SA с атрибуция
Не связан с StackOverflow
scroll top