深入分析android中的Cursor 对象是如何获得的

时间:2021-02-13 22:55:05

    最近在开发一个app应用的时候,需要用到数据库,以前都只是调用了接口,完成了功能,就没有关注过太多的细节东西,今天去仔细的分析了android中创建 Cursor对象的源码,和大家分享一下。

  首先需要定义一个Cursor对象cursor,在定义一个SQLiteDataBase对象db,代码如下:

       cursor = db.rawQuery(sql, selectionArgs);

跟着追踪回源码:

 public Cursor rawQuery(String sql, String[] selectionArgs)

 {     //调用rawQueryWithFactory函数,函数定义如下
        return rawQueryWithFactory(null, sql, selectionArgs, null);
   }

 public Cursor rawQueryWithFactory(
            CursorFactory cursorFactory, String sql, String[] selectionArgs,
            String editTable) {
        verifyDbIsOpen();
        BlockGuard.getThreadPolicy().onReadFromDisk();
        long timeStart = 0;


        if (false || mSlowQueryThreshold != -1) {
            timeStart = System.currentTimeMillis();
        }


        SQLiteDatabase db = getDbConnection(sql);

       //此处又是分析的重点,需要追踪回SQLiteDirectCursorDriver类
        SQLiteCursorDriver driver = new SQLiteDirectCursorDriver(db, sql, editTable);


        Cursor cursor = null;
        try {

       //此处才是获得的cursor对象driver.query方法只是对SQLiteCursorDriver接口中的方法的实现

            cursor = driver.query(
                    cursorFactory != null ? cursorFactory : mFactory,
                    selectionArgs);
        } finally {
            if (false || mSlowQueryThreshold != -1) {


                // Force query execution
                int count = -1;
                if (cursor != null) {
                    count = cursor.getCount();
                }


                long duration = System.currentTimeMillis() - timeStart;


                if (false || duration >= mSlowQueryThreshold) {
                    Log.v(SQLiteCursor.TAG,
                          "query (" + duration + " ms): " + driver.toString() + ", args are "
                                  + (selectionArgs != null
                                  ? TextUtils.join(",", selectionArgs)
                                  : "<null>")  + ", count is " + count);
                }
            }
            releaseDbConnection(db);
        }
        return cursor;

 //query方法的实现如下

  public Cursor query(CursorFactory factory, String[] selectionArgs) {
     
        SQLiteQuery query = null;


        try {
            mDatabase.lock(mSql);
            mDatabase.closePendingStatements();
            query = new SQLiteQuery(mDatabase, mSql, 0, selectionArgs);


            // Create the cursor
            if (factory == null) {

             //此处又创建一个SQLiteCursor对象,通过这个对象的创建,最终获得。
                mCursor = new SQLiteCursor(this, mEditTable, query);
            } else {
                mCursor = factory.newCursor(mDatabase, this, mEditTable, query);
            }


            mQuery = query;
            query = null;
            return mCursor;
        } finally {
            // Make sure this object is cleaned up if something happens
            if (query != null) query.close();
            mDatabase.unlock();
        }
    }
    }

//其实归根结底Cursor中接口的方法在此处定义

public abstract class AbstractCursor implements CrossProcessCursor {
    private static final String TAG = "Cursor";


    DataSetObservable mDataSetObservable = new DataSetObservable();
    ContentObservable mContentObservable = new ContentObservable();


    Bundle mExtras = Bundle.EMPTY;


    /* -------------------------------------------------------- */
    /* These need to be implemented by subclasses */
    abstract public int getCount();


    abstract public String[] getColumnNames();


    abstract public String getString(int column);
    abstract public short getShort(int column);
    abstract public int getInt(int column);
    abstract public long getLong(int column);
    abstract public float getFloat(int column);
    abstract public double getDouble(int column);
    abstract public boolean isNull(int column);


    public int getType(int column) {
        throw new UnsupportedOperationException();
    }


    // TODO implement getBlob in all cursor types
    public byte[] getBlob(int column) {
        throw new UnsupportedOperationException("getBlob is not supported");
    }
    /* -------------------------------------------------------- */
    /* Methods that may optionally be implemented by subclasses */


    /**
     * If the cursor is backed by a {@link CursorWindow}, returns a pre-filled
     * window with the contents of the cursor, otherwise null.
     *
     * @return The pre-filled window that backs this cursor, or null if none.
     */
    public CursorWindow getWindow() {
        return null;
    }


    public int getColumnCount() {
        return getColumnNames().length;
    }


    public void deactivate() {
        onDeactivateOrClose();
    }


    /** @hide */
    protected void onDeactivateOrClose() {
        if (mSelfObserver != null) {
            mContentResolver.unregisterContentObserver(mSelfObserver);
            mSelfObserverRegistered = false;
        }
        mDataSetObservable.notifyInvalidated();
    }


    public boolean requery() {
        if (mSelfObserver != null && mSelfObserverRegistered == false) {
            mContentResolver.registerContentObserver(mNotifyUri, true, mSelfObserver);
            mSelfObserverRegistered = true;
        }
        mDataSetObservable.notifyChanged();
        return true;
    }


    public boolean isClosed() {
        return mClosed;
    }


    public void close() {
        mClosed = true;
        mContentObservable.unregisterAll();
        onDeactivateOrClose();
    }


    /**
     * This function is called every time the cursor is successfully scrolled
     * to a new position, giving the subclass a chance to update any state it
     * may have. If it returns false the move function will also do so and the
     * cursor will scroll to the beforeFirst position.
     *
     * @param oldPosition the position that we're moving from
     * @param newPosition the position that we're moving to
     * @return true if the move is successful, false otherwise
     */
    public boolean onMove(int oldPosition, int newPosition) {
        return true;
    }




    public void copyStringToBuffer(int columnIndex, CharArrayBuffer buffer) {
        // Default implementation, uses getString
        String result = getString(columnIndex);
        if (result != null) {
            char[] data = buffer.data;
            if (data == null || data.length < result.length()) {
                buffer.data = result.toCharArray();
            } else {
                result.getChars(0, result.length(), data, 0);
            }
            buffer.sizeCopied = result.length();
        } else {
            buffer.sizeCopied = 0;
        }
    }


    /* -------------------------------------------------------- */
    /* Implementation */
    public AbstractCursor() {
        mPos = -1;
        mRowIdColumnIndex = -1;
        mCurrentRowID = null;
        mUpdatedRows = new HashMap<Long, Map<String, Object>>();
    }


    public final int getPosition() {
        return mPos;
    }


    public final boolean moveToPosition(int position) {
        // Make sure position isn't past the end of the cursor
        final int count = getCount();
        if (position >= count) {
            mPos = count;
            return false;
        }


        // Make sure position isn't before the beginning of the cursor
        if (position < 0) {
            mPos = -1;
            return false;
        }


        // Check for no-op moves, and skip the rest of the work for them
        if (position == mPos) {
            return true;
        }


        boolean result = onMove(mPos, position);
        if (result == false) {
            mPos = -1;
        } else {
            mPos = position;
            if (mRowIdColumnIndex != -1) {
                mCurrentRowID = Long.valueOf(getLong(mRowIdColumnIndex));
            }
        }


        return result;
    }


    /**
     * Copy data from cursor to CursorWindow
     * @param position start position of data
     * @param window
     */
    public void fillWindow(int position, CursorWindow window) {
        if (position < 0 || position >= getCount()) {
            return;
        }
        window.acquireReference();
        try {
            int oldpos = mPos;
            mPos = position - 1;
            window.clear();
            window.setStartPosition(position);
            int columnNum = getColumnCount();
            window.setNumColumns(columnNum);
            while (moveToNext() && window.allocRow()) {
                for (int i = 0; i < columnNum; i++) {
                    String field = getString(i);
                    if (field != null) {
                        if (!window.putString(field, mPos, i)) {
                            window.freeLastRow();
                            break;
                        }
                    } else {
                        if (!window.putNull(mPos, i)) {
                            window.freeLastRow();
                            break;
                        }
                    }
                }
            }


            mPos = oldpos;
        } catch (IllegalStateException e){
            // simply ignore it
        } finally {
            window.releaseReference();
        }
    }


    public final boolean move(int offset) {
        return moveToPosition(mPos + offset);
    }


    public final boolean moveToFirst() {
        return moveToPosition(0);
    }


    public final boolean moveToLast() {
        return moveToPosition(getCount() - 1);
    }


    public final boolean moveToNext() {
        return moveToPosition(mPos + 1);
    }


    public final boolean moveToPrevious() {
        return moveToPosition(mPos - 1);
    }


    public final boolean isFirst() {
        return mPos == 0 && getCount() != 0;
    }


    public final boolean isLast() {
        int cnt = getCount();
        return mPos == (cnt - 1) && cnt != 0;
    }


    public final boolean isBeforeFirst() {
        if (getCount() == 0) {
            return true;
        }
        return mPos == -1;
    }


    public final boolean isAfterLast() {
        if (getCount() == 0) {
            return true;
        }
        return mPos == getCount();
    }


    public int getColumnIndex(String columnName) {
        // Hack according to bug 903852
        final int periodIndex = columnName.lastIndexOf('.');
        if (periodIndex != -1) {
            Exception e = new Exception();
            Log.e(TAG, "requesting column name with table name -- " + columnName, e);
            columnName = columnName.substring(periodIndex + 1);
        }


        String columnNames[] = getColumnNames();
        int length = columnNames.length;
        for (int i = 0; i < length; i++) {
            if (columnNames[i].equalsIgnoreCase(columnName)) {
                return i;
            }
        }


        if (false) {
            if (getCount() > 0) {
                Log.w("AbstractCursor", "Unknown column " + columnName);
            }
        }
        return -1;
    }


    public int getColumnIndexOrThrow(String columnName) {
        final int index = getColumnIndex(columnName);
        if (index < 0) {
            throw new IllegalArgumentException("column '" + columnName + "' does not exist");
        }
        return index;
    }


    public String getColumnName(int columnIndex) {
        return getColumnNames()[columnIndex];
    }


    public void registerContentObserver(ContentObserver observer) {
        mContentObservable.registerObserver(observer);
    }


    public void unregisterContentObserver(ContentObserver observer) {
        // cursor will unregister all observers when it close
        if (!mClosed) {
            mContentObservable.unregisterObserver(observer);
        }
    }


    /**
     * This is hidden until the data set change model has been re-evaluated.
     * @hide
     */
    protected void notifyDataSetChange() {
        mDataSetObservable.notifyChanged();
    }


    /**
     * This is hidden until the data set change model has been re-evaluated.
     * @hide
     */
    protected DataSetObservable getDataSetObservable() {
        return mDataSetObservable;


    }


    public void registerDataSetObserver(DataSetObserver observer) {
        mDataSetObservable.registerObserver(observer);
    }


    public void unregisterDataSetObserver(DataSetObserver observer) {
        mDataSetObservable.unregisterObserver(observer);
    }


    /**
     * Subclasses must call this method when they finish committing updates to notify all
     * observers.
     *
     * @param selfChange
     */
    protected void onChange(boolean selfChange) {
        synchronized (mSelfObserverLock) {
            mContentObservable.dispatchChange(selfChange);
            if (mNotifyUri != null && selfChange) {
                mContentResolver.notifyChange(mNotifyUri, mSelfObserver);
            }
        }
    }


    /**
     * Specifies a content URI to watch for changes.
     *
     * @param cr The content resolver from the caller's context.
     * @param notifyUri The URI to watch for changes. This can be a
     * specific row URI, or a base URI for a whole class of content.
     */
    public void setNotificationUri(ContentResolver cr, Uri notifyUri) {
        synchronized (mSelfObserverLock) {
            mNotifyUri = notifyUri;
            mContentResolver = cr;
            if (mSelfObserver != null) {
                mContentResolver.unregisterContentObserver(mSelfObserver);
            }
            mSelfObserver = new SelfContentObserver(this);
            mContentResolver.registerContentObserver(mNotifyUri, true, mSelfObserver);
            mSelfObserverRegistered = true;
        }
    }


    public Uri getNotificationUri() {
        return mNotifyUri;
    }


    public boolean getWantsAllOnMoveCalls() {
        return false;
    }


    /**
     * Sets a {@link Bundle} that will be returned by {@link #getExtras()}.  <code>null</code> will
     * be converted into {@link Bundle#EMPTY}.
     *
     * @param extras {@link Bundle} to set.
     * @hide
     */
    public void setExtras(Bundle extras) {
        mExtras = (extras == null) ? Bundle.EMPTY : extras;
    }


    public Bundle getExtras() {
        return mExtras;
    }


    public Bundle respond(Bundle extras) {
        return Bundle.EMPTY;
    }


    /**
     * @deprecated Always returns false since Cursors do not support updating rows
     */
    @Deprecated
    protected boolean isFieldUpdated(int columnIndex) {
        return false;
    }


    /**
     * @deprecated Always returns null since Cursors do not support updating rows
     */
    @Deprecated
    protected Object getUpdatedField(int columnIndex) {
        return null;
    }


    /**
     * This function throws CursorIndexOutOfBoundsException if
     * the cursor position is out of bounds. Subclass implementations of
     * the get functions should call this before attempting
     * to retrieve data.
     *
     * @throws CursorIndexOutOfBoundsException
     */
    protected void checkPosition() {
        if (-1 == mPos || getCount() == mPos) {
            throw new CursorIndexOutOfBoundsException(mPos, getCount());
        }
    }


    @Override
    protected void finalize() {
        if (mSelfObserver != null && mSelfObserverRegistered == true) {
            mContentResolver.unregisterContentObserver(mSelfObserver);
        }
    }


    /**
     * Cursors use this class to track changes others make to their URI.
     */
    protected static class SelfContentObserver extends ContentObserver {
        WeakReference<AbstractCursor> mCursor;


        public SelfContentObserver(AbstractCursor cursor) {
            super(null);
            mCursor = new WeakReference<AbstractCursor>(cursor);
        }


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


        @Override
        public void onChange(boolean selfChange) {
            AbstractCursor cursor = mCursor.get();
            if (cursor != null) {
                cursor.onChange(false);
            }
        }
    }


    /**
     * @deprecated This is never updated by this class and should not be used
     */
    @Deprecated
    protected HashMap<Long, Map<String, Object>> mUpdatedRows;


    /**
     * This must be set to the index of the row ID column by any
     * subclass that wishes to support updates.
     */
    protected int mRowIdColumnIndex;


    protected int mPos;
    /**
     * If {@link #mRowIdColumnIndex} is not -1 this contains contains the value of
     * the column at {@link #mRowIdColumnIndex} for the current row this cursor is
     * pointing at.
     */
    protected Long mCurrentRowID;
    protected ContentResolver mContentResolver;
    protected boolean mClosed = false;
    private Uri mNotifyUri;
    private ContentObserver mSelfObserver;
    final private Object mSelfObserverLock = new Object();
    private boolean mSelfObserverRegistered;
}