How to clone or freeze an Android database cursor

2019-02-15 18:04发布

问题:

I have a custom cursor adapter, and I would like to pass each 'row' of the cursor back to the application (via a registered callback which is working).

I know I could read each of the fields from the cursor and do it manually, but I would simply like to pass a 'frozen clone' of the cursor back. (Reading the fields in the adapter would require me to make several specialised versions of this class.)

Ideally I would like to pass back something with the same interface as Cursor, but which couldn't traverse the result set.

The queries return fewer than 20 rows, so space is not an issue.

回答1:

I guess you have a cursor with 20 rows and now you want to invoke a method 20 times with a cursor that contains only one row. Here is how you can do this:

Cursor c = ...;// contains many rows
if(c.moveToFirst()){
    String[] columns = c.getColumnNames();
    while(!c.isAfterLast()){ 
        MatrixCursor newCursor = new MatrixCursor(columns , 1);
        MatrixCursor.RowBuilder b = newCursor.newRow();
        for(String col: columns){
             // in case all columns are of string type. But if they are 
             // different then see my comment below 
             b.add(c.getString(c.getColumnIndex(col)));
        }
     // invoke your listener here with newCursor
    }
}

What if data type of columns is not String?

For API>=11: Just call getType() method in for loop and use switch statement to invoke appropriate get method.

For API<11: Run another query similar to this PRAGMA table_info(my_table_name) and then just fill a Map of column name and type and use it in for loop. Here is how you can read this cursor https://stackoverflow.com/a/9354401/1112882



回答2:

Consider you have a cursor named data and cursor have columns "title" and "author" and both these columns have string values. The below code shows how to copy cursor named data to cursor named matrixCursor.

String[] PROJECTION = new String[]{"title","author"};
MatrixCursor matrixCursor = new MatrixCursor(PROJECTION);
int i = data.getColumnCount();
if (data.moveToFirst()) {
                do {
                Object[] currRow = new Object[i];
                currRow[0] = data.getString(0);
                currRow[1] = data.getString(1);
                matrixCursor.addRow(currRow);
                } while (data.moveToNext());
            }


回答3:

Based on Anand's answer, this should work without having to specify the column names / projection:

public static Cursor cloneCursor(Cursor oldCursor) {

    if (oldCursor == null) {

        return null;

    }
    else {

        /**
         * Remember the cursor position
         */
        int originalCursorPosition = oldCursor.getPosition();

        String[] projection = oldCursor.getColumnNames();
        MatrixCursor newCursor = new MatrixCursor(projection);

        int numColumns = oldCursor.getColumnCount();

        while (oldCursor.moveToNext()) {

            /**
             * Create the new row object
             */
            Object[] newRow = new Object[numColumns];

            /**
             * Populate each column in the new row
             */
            for (int columnIndex = 0; columnIndex < numColumns; columnIndex++) {

                /**
                 * Detect the field type
                 */
                int fieldType = oldCursor.getType(columnIndex);

                /**
                 * Use the field type to populate the row correctly
                 */
                if (fieldType == Cursor.FIELD_TYPE_BLOB) {
                    newRow[columnIndex] = oldCursor.getBlob(columnIndex);
                }
                else if (fieldType == Cursor.FIELD_TYPE_FLOAT) {
                    newRow[columnIndex] = oldCursor.getDouble(columnIndex);
                }
                else if (fieldType == Cursor.FIELD_TYPE_INTEGER) {
                    newRow[columnIndex] = oldCursor.getLong(columnIndex);
                }
                else if (fieldType == Cursor.FIELD_TYPE_STRING) {
                    newRow[columnIndex] = oldCursor.getString(columnIndex);
                }
                else if (fieldType == Cursor.FIELD_TYPE_NULL) {
                    newRow[columnIndex] = null;
                }
                else {
                    throw new RuntimeException("Unknown fieldType (" + fieldType + ") for column" + columnIndex);
                }

            }

            /**
             * Add the new row to the new cursor
             */
            newCursor.addRow(newRow);

        }

        /**
         * Move both cursors to the position that oldCursor was in before this method was called
         */
        oldCursor.moveToPosition(originalCursorPosition);
        newCursor.moveToPosition(originalCursorPosition);

        /**
         * Return the cloned cursor
         */
        return newCursor;

    }

}

UPDATE

getInt(...) changed to getLong(...).

getFloat(...) changed to getDouble(...)