Frage

Ich frage den CallLog-Inhaltsanbieter ab und muss die Spaltentypen erkennen.

In Honeycomb und neueren Versionen (API Level 11+) können Sie durch Aufrufen der Methode einen bevorzugten Spaltendatentyp abrufen Cursor.getType(int columnIndex) was einen der folgenden Typen zurückgibt:

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

Wie kann ich dies auf Geräten vor Honeycomb <11 erreichen?

Ich habe Folgendes versucht:

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

}

Es wird jedoch keine Ausnahme ausgelöst.Die Daten werden immer in den ersten Typ umgewandelt, nach dem Sie suchen, in diesem Fall getInt().Das heißt, ich erhalte die richtigen Werte, wenn der Spaltentyp lautet Ganze Zahl aber ein 0 für alle anderen Typen.

Warum schaue ich nicht in der Dokumentation nach, um zu überprüfen, welcher Typ gespeichert ist?Die Spalten unterscheiden sich je nach Gerätehersteller und nicht alle sind dokumentiert, siehe diese Frage: Wie gehe ich mit herstellerabhängigen Unterschieden bei ContentProvidern um?

Irgendwelche Ideen?

War es hilfreich?

Lösung

Sie können diesen Code verwenden, wenn sich der Cursor in einer gültigen Zeile befindet:

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

Beachten Sie, dass Cursor.FIELD_TYPE_*-Konstantenwerte beginnend mit HONEYCOMB definiert werden.

Andere Tipps

Ergänzend zu Juans Antwort ist hier mein Ersatz für die API 11-Methode Cursor.getType(int i) – für einen Cursor, der durch eine SQL-Abfrage neu abgestimmt wird

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

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

Es gibt etwas, das funktionieren könnte:http://developer.android.com/reference/android/database/DatabaseUtils.html CursorRowToContentValues

kopiert die Zeile in ein ContentValues-Objekt.Anschließend können Sie ContentValues.get() aufrufen, wodurch Sie ein Objekt erhalten.Sie können sich dann die Klasse dieses Objekts ansehen.

bearbeiten

Laut Quellcode von DatabaseUtils handelt es sich bei den Objekten entweder um Blobs oder Strings.

bearbeiten 2

Wenn Ihr Cursor jedoch ein WindowedCursor ist, verfügt er über Methoden zum Erkennen der Objekttypen.(isBlob, isString, isLong...)

Ich hatte in der Vergangenheit das gleiche Problem.Ich habe es mit einer ziemlich guten Lösung in Angriff genommen.Passen Sie dies an Ihre Bedürfnisse an.In meinem Fall habe ich eine Menge verschiedener Objekte, die alle mit einem Server in der Cloud synchronisiert werden.Sie haben alle gemeinsame Eigenschaften, sodass sie alle von einem gemeinsamen BaseObject erben.Dieses Objekt verfügt über eine Methode, die einen Cursor als Parameter verwendet und ein neues Objekt desselben Typs zurückgibt, sodass jedes Objekt, das von ihm erbt, diese Methode mit seinen erweiterten Eigenschaften überschreibt.

*Beachten Sie, dass die Vererbung von Objekten für diesen Ansatz nicht erforderlich ist.Es ist einfach eine intelligentere Art, es zu tun.Solange Sie in allen Objekten, die Sie zur Übernahme aus der Datenbank benötigen, die gleiche Methode haben, wird dies funktionieren, wie Sie am Ende sehen werden.

Lassen Sie mich das veranschaulichen:

Unser Basisobjekt.

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

Ein neues Objekt, das vom ersten erbt.

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

Schließlich ist es in Ihrer Datenbank so einfach, nach den gewünschten Daten zu fragen, indem Sie eine generische Methode „getAllObjects“ aufrufen und den Klassentyp, den Sie abfragen möchten, zusammen mit den anderen Parametern der Abfrage übergeben:

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

Und los geht's.Eine generische Methode, um ein beliebiges Objekt aus Ihrer Datenbank abzurufen und es zur Laufzeit erfolgreich in ein Objekt oder ein Array von Objekten umzuwandeln.

Anmerkungen:

  • Jedes Objekt sollte eine Methode getTable() haben, um die richtige Tabelle abfragen zu können
  • Wie Sie vielleicht sehen, gibt es bei diesem Ansatz auch eine OODB-Verbindung.Sie können den gleichen Ansatz auch ohne verwenden, indem Sie einfach alle Elemente abfragen (SELECT * FROM...)

Ich hoffe es hilft.Antworten Sie bei Problemen oder Zweifeln.

Lizenziert unter: CC-BY-SA mit Zuschreibung
Nicht verbunden mit StackOverflow
scroll top