diff options
Diffstat (limited to 'core/java/android')
-rw-r--r-- | core/java/android/database/AbstractCursor.java | 5 | ||||
-rw-r--r-- | core/java/android/database/AbstractWindowedCursor.java | 53 | ||||
-rw-r--r-- | core/java/android/database/BulkCursorToCursorAdaptor.java | 5 | ||||
-rw-r--r-- | core/java/android/database/CursorWindow.java | 20 | ||||
-rw-r--r-- | core/java/android/database/CursorWrapper.java | 4 | ||||
-rw-r--r-- | core/java/android/database/sqlite/SQLiteCursor.java | 194 | ||||
-rw-r--r-- | core/java/android/database/sqlite/SQLiteDatabase.java | 26 | ||||
-rw-r--r-- | core/java/android/database/sqlite/SQLiteQuery.java | 30 |
8 files changed, 87 insertions, 250 deletions
diff --git a/core/java/android/database/AbstractCursor.java b/core/java/android/database/AbstractCursor.java index 5fe42db..3f23b89 100644 --- a/core/java/android/database/AbstractCursor.java +++ b/core/java/android/database/AbstractCursor.java @@ -64,7 +64,10 @@ public abstract class AbstractCursor implements CrossProcessCursor { /* Methods that may optionally be implemented by subclasses */ /** - * returns a pre-filled window, return NULL if no such window + * 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; diff --git a/core/java/android/database/AbstractWindowedCursor.java b/core/java/android/database/AbstractWindowedCursor.java index 3d95769..bfc8123 100644 --- a/core/java/android/database/AbstractWindowedCursor.java +++ b/core/java/android/database/AbstractWindowedCursor.java @@ -18,8 +18,22 @@ package android.database; /** * A base class for Cursors that store their data in {@link CursorWindow}s. + * <p> + * Subclasses are responsible for filling the cursor window with data during + * {@link #onMove(int, int)}, allocating a new cursor window if necessary. + * During {@link #requery()}, the existing cursor window should be cleared and + * filled with new data. + * </p><p> + * If the contents of the cursor change or become invalid, the old window must be closed + * (because it is owned by the cursor) and set to null. + * </p> */ public abstract class AbstractWindowedCursor extends AbstractCursor { + /** + * The cursor window owned by this cursor. + */ + protected CursorWindow mWindow; + @Override public byte[] getBlob(int columnIndex) { checkPosition(); @@ -126,25 +140,44 @@ public abstract class AbstractWindowedCursor extends AbstractCursor { public CursorWindow getWindow() { return mWindow; } - + /** - * Set a new cursor window to cursor, usually set a remote cursor window - * @param window cursor window + * Sets a new cursor window for the cursor to use. + * <p> + * The cursor takes ownership of the provided cursor window; the cursor window + * will be closed when the cursor is closed or when the cursor adopts a new + * cursor window. + * </p><p> + * If the cursor previously had a cursor window, then it is closed when the + * new cursor window is assigned. + * </p> + * + * @param window The new cursor window, typically a remote cursor window. */ public void setWindow(CursorWindow window) { - if (mWindow != null) { - mWindow.close(); + if (window != mWindow) { + closeWindow(); + mWindow = window; } - mWindow = window; } - + + /** + * Returns true if the cursor has an associated cursor window. + * + * @return True if the cursor has an associated cursor window. + */ public boolean hasWindow() { return mWindow != null; } /** - * This needs be updated in {@link #onMove} by subclasses, and - * needs to be set to NULL when the contents of the cursor change. + * Closes the cursor window and sets {@link #mWindow} to null. + * @hide */ - protected CursorWindow mWindow; + protected void closeWindow() { + if (mWindow != null) { + mWindow.close(); + mWindow = null; + } + } } diff --git a/core/java/android/database/BulkCursorToCursorAdaptor.java b/core/java/android/database/BulkCursorToCursorAdaptor.java index 16becf5..9c1b26d 100644 --- a/core/java/android/database/BulkCursorToCursorAdaptor.java +++ b/core/java/android/database/BulkCursorToCursorAdaptor.java @@ -154,10 +154,7 @@ public final class BulkCursorToCursorAdaptor extends AbstractWindowedCursor { false /* the window will be accessed across processes */)); if (mCount != -1) { mPos = -1; - if (mWindow != null) { - mWindow.close(); - mWindow = null; - } + closeWindow(); // super.requery() will call onChanged. Do it here instead of relying on the // observer from the far side so that observers can see a correct value for mCount diff --git a/core/java/android/database/CursorWindow.java b/core/java/android/database/CursorWindow.java index 183662f..2e3ef28 100644 --- a/core/java/android/database/CursorWindow.java +++ b/core/java/android/database/CursorWindow.java @@ -105,8 +105,12 @@ public class CursorWindow extends SQLiteClosable implements Parcelable { } @Override - protected void finalize() { - dispose(); + protected void finalize() throws Throwable { + try { + dispose(); + } finally { + super.finalize(); + } } private void dispose() { @@ -145,10 +149,12 @@ public class CursorWindow extends SQLiteClosable implements Parcelable { /** * Gets the start position of this cursor window. - * The start position is the index of the first row that this window contains + * <p> + * The start position is the zero-based index of the first row that this window contains * relative to the entire result set of the {@link Cursor}. + * </p> * - * @return The start position. + * @return The zero-based start position. */ public int getStartPosition() { return mStartPos; @@ -156,10 +162,12 @@ public class CursorWindow extends SQLiteClosable implements Parcelable { /** * Sets the start position of this cursor window. - * The start position is the index of the first row that this window contains + * <p> + * The start position is the zero-based index of the first row that this window contains * relative to the entire result set of the {@link Cursor}. + * </p> * - * @param pos The new start position. + * @param pos The new zero-based start position. */ public void setStartPosition(int pos) { mStartPos = pos; diff --git a/core/java/android/database/CursorWrapper.java b/core/java/android/database/CursorWrapper.java index 3c3bd43..320733e 100644 --- a/core/java/android/database/CursorWrapper.java +++ b/core/java/android/database/CursorWrapper.java @@ -33,7 +33,9 @@ public class CursorWrapper implements Cursor { } /** - * @return the wrapped cursor + * Gets the underlying cursor that is wrapped by this instance. + * + * @return The wrapped cursor. */ public Cursor getWrappedCursor() { return mCursor; diff --git a/core/java/android/database/sqlite/SQLiteCursor.java b/core/java/android/database/sqlite/SQLiteCursor.java index ea9346d..81fe824 100644 --- a/core/java/android/database/sqlite/SQLiteCursor.java +++ b/core/java/android/database/sqlite/SQLiteCursor.java @@ -18,16 +18,11 @@ package android.database.sqlite; import android.database.AbstractWindowedCursor; import android.database.CursorWindow; -import android.database.DataSetObserver; -import android.os.Handler; -import android.os.Message; -import android.os.Process; import android.os.StrictMode; import android.util.Log; import java.util.HashMap; import java.util.Map; -import java.util.concurrent.locks.ReentrantLock; /** * A Cursor implementation that exposes results from a query on a @@ -60,140 +55,8 @@ public class SQLiteCursor extends AbstractWindowedCursor { /** Used to find out where a cursor was allocated in case it never got released. */ private final Throwable mStackTrace; - - /** - * mMaxRead is the max items that each cursor window reads - * default to a very high value - */ - private int mMaxRead = Integer.MAX_VALUE; - private int mInitialRead = Integer.MAX_VALUE; - private int mCursorState = 0; - private ReentrantLock mLock = null; - private boolean mPendingData = false; /** - * support for a cursor variant that doesn't always read all results - * initialRead is the initial number of items that cursor window reads - * if query contains more than this number of items, a thread will be - * created and handle the left over items so that caller can show - * results as soon as possible - * @param initialRead initial number of items that cursor read - * @param maxRead leftover items read at maxRead items per time - * @hide - */ - public void setLoadStyle(int initialRead, int maxRead) { - mMaxRead = maxRead; - mInitialRead = initialRead; - mLock = new ReentrantLock(true); - } - - private void queryThreadLock() { - if (mLock != null) { - mLock.lock(); - } - } - - private void queryThreadUnlock() { - if (mLock != null) { - mLock.unlock(); - } - } - - - /** - * @hide - */ - final private class QueryThread implements Runnable { - private final int mThreadState; - QueryThread(int version) { - mThreadState = version; - } - private void sendMessage() { - if (mNotificationHandler != null) { - mNotificationHandler.sendEmptyMessage(1); - mPendingData = false; - } else { - mPendingData = true; - } - - } - public void run() { - // use cached mWindow, to avoid get null mWindow - CursorWindow cw = mWindow; - Process.setThreadPriority(Process.myTid(), Process.THREAD_PRIORITY_BACKGROUND); - // the cursor's state doesn't change - while (true) { - mLock.lock(); - try { - if (mCursorState != mThreadState) { - break; - } - - int count = getQuery().fillWindow(cw, mMaxRead, mCount); - // return -1 means there is still more data to be retrieved from the resultset - if (count != 0) { - if (count == NO_COUNT){ - mCount += mMaxRead; - if (Log.isLoggable(TAG, Log.DEBUG)) { - Log.d(TAG, "received -1 from native_fill_window. read " + - mCount + " rows so far"); - } - sendMessage(); - } else { - mCount += count; - if (Log.isLoggable(TAG, Log.DEBUG)) { - Log.d(TAG, "received all data from native_fill_window. read " + - mCount + " rows."); - } - sendMessage(); - break; - } - } else { - break; - } - } catch (Exception e) { - // end the tread when the cursor is close - break; - } finally { - mLock.unlock(); - } - } - } - } - - /** - * @hide - */ - protected class MainThreadNotificationHandler extends Handler { - public void handleMessage(Message msg) { - notifyDataSetChange(); - } - } - - /** - * @hide - */ - protected MainThreadNotificationHandler mNotificationHandler; - - public void registerDataSetObserver(DataSetObserver observer) { - super.registerDataSetObserver(observer); - if ((Integer.MAX_VALUE != mMaxRead || Integer.MAX_VALUE != mInitialRead) && - mNotificationHandler == null) { - queryThreadLock(); - try { - mNotificationHandler = new MainThreadNotificationHandler(); - if (mPendingData) { - notifyDataSetChange(); - mPendingData = false; - } - } finally { - queryThreadUnlock(); - } - } - - } - - /** * Execute a query and provide access to its result set through a Cursor * interface. For a query such as: {@code SELECT name, birth, phone FROM * myTable WHERE ... LIMIT 1,20 ORDER BY...} the column names (name, birth, @@ -293,36 +156,23 @@ public class SQLiteCursor extends AbstractWindowedCursor { return mCount; } - private void fillWindow (int startPos) { + private void fillWindow(int startPos) { if (mWindow == null) { // If there isn't a window set already it will only be accessed locally mWindow = new CursorWindow(true /* the window is local only */); } else { - mCursorState++; - queryThreadLock(); - try { - mWindow.clear(); - } finally { - queryThreadUnlock(); - } + mWindow.clear(); } mWindow.setStartPosition(startPos); - int count = getQuery().fillWindow(mWindow, mInitialRead, 0); - // return -1 means there is still more data to be retrieved from the resultset - if (count == NO_COUNT){ - mCount = startPos + mInitialRead; - if (Log.isLoggable(TAG, Log.DEBUG)) { - Log.d(TAG, "received -1 from native_fill_window. read " + mCount + " rows so far"); - } - Thread t = new Thread(new QueryThread(mCursorState), "query thread"); - t.start(); - } else if (startPos == 0) { // native_fill_window returns count(*) only for startPos = 0 + int count = getQuery().fillWindow(mWindow); + if (startPos == 0) { // fillWindow returns count(*) only for startPos = 0 if (Log.isLoggable(TAG, Log.DEBUG)) { Log.d(TAG, "received count(*) from native_fill_window: " + count); } mCount = count; } else if (mCount <= 0) { - throw new IllegalStateException("count should never be non-zero negative number"); + throw new IllegalStateException("Row count should never be zero or negative " + + "when the start position is non-zero"); } } @@ -366,11 +216,7 @@ public class SQLiteCursor extends AbstractWindowedCursor { private void deactivateCommon() { if (false) Log.v(TAG, "<<< Releasing cursor " + this); - mCursorState = 0; - if (mWindow != null) { - mWindow.close(); - mWindow = null; - } + closeWindow(); if (false) Log.v("DatabaseWindow", "closing window in release()"); } @@ -439,16 +285,12 @@ public class SQLiteCursor extends AbstractWindowedCursor { // This one will recreate the temp table, and get its count mDriver.cursorRequeried(this); mCount = NO_COUNT; - mCursorState++; - queryThreadLock(); try { mQuery.requery(); } catch (IllegalStateException e) { // for backwards compatibility, just return false Log.w(TAG, "requery() failed " + e.getMessage(), e); return false; - } finally { - queryThreadUnlock(); } } @@ -472,18 +314,9 @@ public class SQLiteCursor extends AbstractWindowedCursor { } @Override - public void setWindow(CursorWindow window) { - if (mWindow != null) { - mCursorState++; - queryThreadLock(); - try { - mWindow.close(); - } finally { - queryThreadUnlock(); - } - mCount = NO_COUNT; - } - mWindow = window; + public void setWindow(CursorWindow window) { + super.setWindow(window); + mCount = NO_COUNT; } /** @@ -521,11 +354,4 @@ public class SQLiteCursor extends AbstractWindowedCursor { super.finalize(); } } - - /** - * this is only for testing purposes. - */ - /* package */ int getMCount() { - return mCount; - } } diff --git a/core/java/android/database/sqlite/SQLiteDatabase.java b/core/java/android/database/sqlite/SQLiteDatabase.java index d23873d..00d7ce8 100644 --- a/core/java/android/database/sqlite/SQLiteDatabase.java +++ b/core/java/android/database/sqlite/SQLiteDatabase.java @@ -1598,32 +1598,6 @@ public class SQLiteDatabase extends SQLiteClosable { } /** - * Runs the provided SQL and returns a cursor over the result set. - * The cursor will read an initial set of rows and the return to the caller. - * It will continue to read in batches and send data changed notifications - * when the later batches are ready. - * @param sql the SQL query. The SQL string must not be ; terminated - * @param selectionArgs You may include ?s in where clause in the query, - * which will be replaced by the values from selectionArgs. The - * values will be bound as Strings. - * @param initialRead set the initial count of items to read from the cursor - * @param maxRead set the count of items to read on each iteration after the first - * @return A {@link Cursor} object, which is positioned before the first entry. Note that - * {@link Cursor}s are not synchronized, see the documentation for more details. - * - * This work is incomplete and not fully tested or reviewed, so currently - * hidden. - * @hide - */ - public Cursor rawQuery(String sql, String[] selectionArgs, - int initialRead, int maxRead) { - SQLiteCursor c = (SQLiteCursor)rawQueryWithFactory( - null, sql, selectionArgs, null); - c.setLoadStyle(initialRead, maxRead); - return c; - } - - /** * Convenience method for inserting a row into the database. * * @param table the table to insert the row into diff --git a/core/java/android/database/sqlite/SQLiteQuery.java b/core/java/android/database/sqlite/SQLiteQuery.java index 06a41b2..7db0914 100644 --- a/core/java/android/database/sqlite/SQLiteQuery.java +++ b/core/java/android/database/sqlite/SQLiteQuery.java @@ -30,6 +30,11 @@ import android.util.Log; public class SQLiteQuery extends SQLiteProgram { private static final String TAG = "SQLiteQuery"; + private static native int nativeFillWindow(int databasePtr, int statementPtr, int windowPtr, + int startPos, int offsetParam); + private static native int nativeColumnCount(int statementPtr); + private static native String nativeColumnName(int statementPtr, int columnIndex); + /** The index of the unbound OFFSET parameter */ private int mOffsetIndex = 0; @@ -68,19 +73,15 @@ public class SQLiteQuery extends SQLiteProgram { * @param window The window to fill into * @return number of total rows in the query */ - /* package */ int fillWindow(CursorWindow window, - int maxRead, int lastPos) { + /* package */ int fillWindow(CursorWindow window) { mDatabase.lock(mSql); long timeStart = SystemClock.uptimeMillis(); try { acquireReference(); try { window.acquireReference(); - // if the start pos is not equal to 0, then most likely window is - // too small for the data set, loading by another thread - // is not safe in this situation. the native code will ignore maxRead - int numRows = native_fill_window(window.mWindowPtr, window.getStartPosition(), - mOffsetIndex, maxRead, lastPos); + int numRows = nativeFillWindow(nHandle, nStatement, window.mWindowPtr, + window.getStartPosition(), mOffsetIndex); mDatabase.logTimeStat(mSql, timeStart); return numRows; } catch (IllegalStateException e){ @@ -111,7 +112,7 @@ public class SQLiteQuery extends SQLiteProgram { /* package */ int columnCountLocked() { acquireReference(); try { - return native_column_count(); + return nativeColumnCount(nStatement); } finally { releaseReference(); } @@ -127,17 +128,17 @@ public class SQLiteQuery extends SQLiteProgram { /* package */ String columnNameLocked(int columnIndex) { acquireReference(); try { - return native_column_name(columnIndex); + return nativeColumnName(nStatement, columnIndex); } finally { releaseReference(); } } - + @Override public String toString() { return "SQLiteQuery: " + mSql; } - + @Override public void close() { super.close(); @@ -153,11 +154,4 @@ public class SQLiteQuery extends SQLiteProgram { } compileAndbindAllArgs(); } - - private final native int native_fill_window(int windowPtr, - int startPos, int offsetParam, int maxRead, int lastPos); - - private final native int native_column_count(); - - private final native String native_column_name(int columnIndex); } |