0

我有这个 Cursor 实现(API 10):

package com.blablabla.android.helpers.db.cursor;

import android.content.ContentResolver;
import android.database.AbstractWindowedCursor;
import android.database.CharArrayBuffer;
import android.database.ContentObserver;
import android.database.CrossProcessCursor;
import android.database.CursorWindow;
import android.database.DataSetObserver;
import android.net.Uri;
import android.os.Bundle;
import android.os.Parcel;
import android.os.Parcelable;
import android.util.Log;

import com.blablabla.android.helpers.classloader.ClassLoaderHelper;
import com.blablabla.android.helpers.util.BijectiveHashMap;
import com.blablabla.android.helpers.util.BijectiveMap;

/**
 * Cursor for IPC. Takes a CursorWindow as data buffer and the number of columns
 * that CursorWindow has.
 * 
 * @author me@blablabla.eu
 * 
 */
public class ParcelableCursor implements Parcelable, CrossProcessCursor {

    /** Cursor data window */
    protected CursorWindow window = CursorHelper.getCursorWindowInstance();

    /** How many columns we have */
    protected int numColumns = 0;

    /** Column names */
    protected BijectiveMap<String, Integer> colNames = new BijectiveHashMap<String, Integer>();

    /** Current row */
    protected int curRow = -1;

    /** Is this cursor closed? */
    protected boolean closed = false;

    public ParcelableCursor() {
    }

    // /////////////////
    // PARCELABLE IMPLEMENTATION
    // /////////////////

    /** CREATOR for Parcelable */
    public static final Parcelable.Creator<ParcelableCursor> CREATOR = new Parcelable.Creator<ParcelableCursor>() {
        public ParcelableCursor createFromParcel(Parcel in) {
            return new ParcelableCursor(in);
        }

        public ParcelableCursor[] newArray(int size) {
            return new ParcelableCursor[size];
        }
    };

    /** Constructor for Parcelable */
    public ParcelableCursor(Parcel in) {
        readFromParcel(in);
    }

    @Override
    public int describeContents() {
        // Nothing to do here
        return 0;
    }

    @Override
    public void writeToParcel(Parcel out, int flags) {
        out.writeParcelable(window, 0);
        out.writeInt(numColumns);
        out.writeParcelable((Parcelable) colNames, 0);
        out.writeInt(curRow);
        out.writeByte(closed ? (byte) 1 : 0);
    }

    /** Restoring this object from a Parcel */
    public void readFromParcel(Parcel in) {
        window = in.readParcelable(CursorWindow.class.getClassLoader());
        numColumns = in.readInt();
        colNames = in.readParcelable(ClassLoaderHelper.getClassLoader());
        curRow = in.readInt();
        closed = (in.readByte() == 1);
    }

    // ////////////////
    // END PARCELABLE IMPLEMENTATION
    // ////////////////

    // /////////////////
    // CROSS PROCESS CURSOR IMPLEMENTATION
    // /////////////////
    @Override
    public void close() {
        window.close();
        closed = true;
    }

    @Override
    public void copyStringToBuffer(int columnIndex, CharArrayBuffer buffer) {
        // TODO: what does this do?
    }

    @Override
    public void deactivate() {
        // Deprecated, does nothing
    }

    @Override
    public byte[] getBlob(int columnIndex) {
        return window.getBlob(curRow, columnIndex);
    }

    @Override
    public int getColumnCount() {
        return numColumns;
    }

    @Override
    public int getColumnIndex(String columnName) {
        int ret = -1;
        Integer col = colNames.get(columnName);
        if (col != null) {
            ret = col;
        }
        return ret;
    }

    @Override
    public int getColumnIndexOrThrow(String columnName)
            throws IllegalArgumentException {
        Integer col = colNames.get(columnName);
        if (col == null) {
            throw new IllegalArgumentException();
        }
        return col;
    }

    @Override
    public String getColumnName(int columnIndex) {
        return colNames.getKey(columnIndex);
    }

    @Override
    public String[] getColumnNames() {
        Log.d("PARCELCURSOR.getColumnNames()", "===GETTING COLNAMES===");
        return colNames.keySet().toArray(new String[colNames.keySet().size()]);
    }

    @Override
    public int getCount() {
        return window.getNumRows();
    }

    @Override
    public double getDouble(int columnIndex) {
        return window.getDouble(curRow, columnIndex);
    }

    @Override
    public Bundle getExtras() {
        // TODO
        return null;
    }

    @Override
    public float getFloat(int columnIndex) {
        return window.getFloat(curRow, columnIndex);
    }

    @Override
    public int getInt(int columnIndex) {
        return window.getInt(curRow, columnIndex);
    }

    @Override
    public long getLong(int columnIndex) {
        return window.getLong(curRow, columnIndex);
    }

    @Override
    public int getPosition() {
        return curRow;
    }

    @Override
    public short getShort(int columnIndex) {
        return window.getShort(curRow, columnIndex);
    }

    @Override
    public String getString(int columnIndex) {
        return window.getString(curRow, columnIndex);
    }

    @Override
    public boolean getWantsAllOnMoveCalls() {
        return false;
    }

    @Override
    public boolean isAfterLast() {
        return (curRow >= window.getNumRows());
    }

    @Override
    public boolean isBeforeFirst() {
        return (curRow < 0);
    }

    @Override
    public boolean isClosed() {
        return closed;
    }

    @Override
    public boolean isFirst() {
        return (curRow == 0);
    }

    @Override
    public boolean isLast() {
        return (curRow == window.getNumRows() - 1);
    }

    @Override
    public boolean isNull(int columnIndex) {
        return window.isNull(curRow, columnIndex);
    }

    @Override
    public boolean move(int offset) {
        int oldPos = curRow;
        curRow += offset;
        if (curRow < -1) {
            curRow = -1;
            return false;
        } else if (curRow > window.getNumRows() - 1) {
            curRow = window.getNumRows() - 1;
            return false;
        }
        return onMove(oldPos, curRow);
    }

    @Override
    public boolean moveToFirst() {
        if (window.getNumRows() == 0) {
            return false;
        }
        int oldPos = curRow;
        curRow = 0;
        return onMove(oldPos, curRow);
    }

    @Override
    public boolean moveToLast() {
        if (window.getNumRows() == 0) {
            return false;
        }
        int oldPos = curRow;
        curRow = window.getNumRows() - 1;
        return onMove(oldPos, curRow);
    }

    @Override
    public boolean moveToNext() {
        int oldPos = curRow++;
        if (isAfterLast()) {
            curRow = window.getNumRows();
            return false;
        }
        return onMove(oldPos, curRow);
    }

    @Override
    public boolean moveToPosition(int position) {
        if (position < -1 && position >= window.getNumRows()) {
            return false;
        }
        int oldPos = curRow;
        curRow = position;
        return onMove(oldPos, curRow);
    }

    @Override
    public boolean moveToPrevious() {
        int oldPos = curRow--;
        if (isBeforeFirst()) {
            curRow = -1;
            return false;
        }
        return onMove(oldPos, curRow);
    }

    /** Not supported */
    @Override
    public void registerContentObserver(ContentObserver observer) {
        // Does nothing
    }

    /** Not supported */
    @Override
    public void registerDataSetObserver(DataSetObserver observer) {
        // Does nothing
    }

    /** Deprecated, not supported */
    @Override
    public boolean requery() {
        return false;
    }

    /** Not supported */
    @Override
    public Bundle respond(Bundle extras) {
        // Does nothing
        return null;
    }

    /** Not supported */
    @Override
    public void setNotificationUri(ContentResolver cr, Uri uri) {
        // Does nothing
    }

    /** Not supported */
    @Override
    public void unregisterContentObserver(ContentObserver observer) {
        // Does nothing
    }

    /** Not supported */
    @Override
    public void unregisterDataSetObserver(DataSetObserver observer) {
        // Does nothing
    }

    @Override
    public void fillWindow(int position, CursorWindow window) {
        CursorHelper.copyCursorWindow(position, this.window, window);
    }

    @Override
    public CursorWindow getWindow() {
        CursorWindow ret = new CursorWindow(false);
        fillWindow(0, ret);
        return ret;
    }

    @Override
    public boolean onMove(int oldPosition, int newPosition) {
        // Don't forget to set curRow = -1 if this method returns false
        return true;
    }

    // /////////////////
    // END CROSS PROCESS CURSOR IMPLEMENTATION
    // /////////////////

    /** Sets this cursor using a CursorWindow data */
    public void setFromWindow(CursorWindow window) {
        CursorHelper.copyCursorWindow(0, window, this.window);
        numColumns = CursorHelper.getCursorWindowNumCols(window);
        moveToPosition(-1);
    }

    /** Sets this cursor from another windowed Cursor */
    public void setFromCursor(AbstractWindowedCursor cursor) {

        // Set column names
        String[] colNames = cursor.getColumnNames();
        for (String col : colNames) {
            addColumn(col);
        }

        // Fill window
        window.setNumColumns(numColumns);
        cursor.fillWindow(0, window);
        moveToPosition(-1);
    }

    /**
     * Adds a new column at the end and assigns it this name. This will make
     * this cursor to lose all its data, so you have to add all the columns
     * before adding any row.
     */
    private void addColumn(String name) {
        numColumns++;
        curRow = -1;
        colNames.put(name, numColumns - 1);
    }
}

Cursor通过Messenger(实例内部Message)和ContentProvider.query()(和朋友)发送了一个实例。问题是该isNull()方法在通过发送时可以正常工作,CursorMessenger在通过发送时总是返回 false ContentProvider

这是我的ContentProvider.query()实现:

@Override
public Cursor query(final Uri uri, final String[] projection,
    final String selection, final String[] selectionArgs,
    final String sortOrder) {

    // Build custom query object
    final Query query = getBasicQuery(uri);
    // .... More code preparing the query object

    // Execute the query
    dbManager.executeQuery(getSourceId(uri), query);

    // This is a ParcelableCursor instance
    final Cursor resultCursor = query.getResultCursor();
    return resultCursor;
}

在这里,当我在ContentProvider进程中运行时,该isNull()方法可以正常工作,但是当Cursor在客户端进程接收到时,它总是返回false.

这是我做 aCursor.getString()因为Cursor.isNull()返回时抛出的异常(错误地)false

08-13 13:17:16.480: D/SELECT on ui(1572): java.lang.IllegalStateException: UNKNOWN type 0
08-13 13:17:16.480: D/SELECT on ui(1572):         at android.database.CursorWindow.getString_native(Native Method)
08-13 13:17:16.480: D/SELECT on ui(1572):         at android.database.CursorWindow.getString(CursorWindow.java:329)
08-13 13:17:16.480: D/SELECT on ui(1572):         at android.database.AbstractWindowedCursor.getString(AbstractWindowedCursor.java:49)
08-13 13:17:16.480: D/SELECT on ui(1572):         at android.database.CursorWrapper.getString(CursorWrapper.java:135)
08-13 13:17:16.480: D/SELECT on ui(1572):         at com.blablabla.android.core.test.TestDBActivity.selectDB(TestDBActivity.java:332)
08-13 13:17:16.480: D/SELECT on ui(1572):         at com.blablabla.android.core.test.TestDBActivity$3.onClick(TestDBActivity.java:169)
08-13 13:17:16.480: D/SELECT on ui(1572):         at android.view.View.performClick(View.java:2485)
08-13 13:17:16.480: D/SELECT on ui(1572):         at android.view.View$PerformClick.run(View.java:9080)
08-13 13:17:16.480: D/SELECT on ui(1572):         at android.os.Handler.handleCallback(Handler.java:587)
08-13 13:17:16.480: D/SELECT on ui(1572):         at android.os.Handler.dispatchMessage(Handler.java:92)
08-13 13:17:16.480: D/SELECT on ui(1572):         at android.os.Looper.loop(Looper.java:123)
08-13 13:17:16.480: D/SELECT on ui(1572):         at android.app.ActivityThread.main(ActivityThread.java:3683)
08-13 13:17:16.480: D/SELECT on ui(1572):         at java.lang.reflect.Method.invokeNative(Native Method)
08-13 13:17:16.480: D/SELECT on ui(1572):         at java.lang.reflect.Method.invoke(Method.java:507)
08-13 13:17:16.480: D/SELECT on ui(1572):         at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:839)
08-13 13:17:16.480: D/SELECT on ui(1572):         at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:597)
08-13 13:17:16.480: D/SELECT on ui(1572):         at dalvik.system.NativeStart.main(Native Method)

编辑:经过大量调试,我可以看到当值为 时nullisBlob()返回true

关于这里可能出了什么问题的任何想法?

4

1 回答 1

0

问题其实出在CursorHelper.copyCursorWindow(),里面调用了fillWindow(),Android系统调用里面CursorWrapper。这是我使用此方法的错误版本:

/**
 * Copies one CursorWindow into another. Previous data in destination
 * CursorWindow is lost
 * 
 * @param position
 *            Starting position to copy from the origin window
 * @param numColumns
 *            Number of columns in the origin window
 * @param origin
 *            CursorWindow to copy from
 * @param destination
 *            CursorWindow to copy to
 */
public static void copyCursorWindow(int position, CursorWindow origin,
        CursorWindow destination) {

    // Column number
    int numCols = getCursorWindowNumCols(origin);

    // Clear destination
    destination.clear();
    destination.setNumColumns(numCols);

    // Rows
    int i = position;
    while (i < origin.getNumRows() && destination.allocRow()) {

        // Columns
        for (int j = 0; j < numCols; j++) {

            if (origin.isBlob(i, j)) {
                byte[] cur = origin.getBlob(i, j);
                destination.putBlob(cur, i, j);

            } else if (origin.isFloat(i, j)) {
                Float cur = origin.getFloat(i, j);
                destination.putDouble(cur, i, j);

            } else if (origin.isLong(i, j)) {
                Long cur = origin.getLong(i, j);
                destination.putLong(cur, i, j);

            } else if (origin.isString(i, j)) {
                String cur = origin.getString(i, j);
                destination.putString(cur, i, j);

            } else if (origin.isNull(i, j)) {
                destination.putNull(i, j);
            }
        }
        i++;
    }
}

这里的问题是isBlob()当值实际上是时会返回 true null。所以isNull()应该是第一次检查。这是一个有效的实现:

/**
 * Copies one CursorWindow into another. Previous data in destination
 * CursorWindow is lost
 * 
 * @param position
 *            Starting position to copy from the origin window
 * @param numColumns
 *            Number of columns in the origin window
 * @param origin
 *            CursorWindow to copy from
 * @param destination
 *            CursorWindow to copy to
 */
public static void copyCursorWindow(int position, CursorWindow origin,
        CursorWindow destination) {

    // Column number
    int numCols = getCursorWindowNumCols(origin);

    // Clear destination
    destination.clear();
    destination.setNumColumns(numCols);

    // Rows
    int i = position;
    while (i < origin.getNumRows() && destination.allocRow()) {

        // Columns
        for (int j = 0; j < numCols; j++) {

            if (origin.isNull(i, j)) {
                destination.putNull(i, j);

            } else if (origin.isFloat(i, j)) {
                Float cur = origin.getFloat(i, j);
                destination.putDouble(cur, i, j);

            } else if (origin.isLong(i, j)) {
                Long cur = origin.getLong(i, j);
                destination.putLong(cur, i, j);

            } else if (origin.isString(i, j)) {
                String cur = origin.getString(i, j);
                destination.putString(cur, i, j);

            } else if (origin.isBlob(i, j)) {
                byte[] cur = origin.getBlob(i, j);
                destination.putBlob(cur, i, j);
            }
        }
        i++;
    }
}

恕我直言,这是一个(烦人的)如何isBlob()实现的错误(尽管可能在 API 11+ 中修复,因为这部分有一些变化——Cursor.getType()比如方法——)。

实施getCursorWindowNumCols()

于 2012-08-13T15:51:24.217 回答