summaryrefslogtreecommitdiffstats
path: root/core/java
diff options
context:
space:
mode:
Diffstat (limited to 'core/java')
-rw-r--r--core/java/android/database/AbstractCursor.java5
-rw-r--r--core/java/android/database/AbstractWindowedCursor.java53
-rw-r--r--core/java/android/database/BulkCursorToCursorAdaptor.java5
-rw-r--r--core/java/android/database/CursorWindow.java20
-rw-r--r--core/java/android/database/CursorWrapper.java4
-rw-r--r--core/java/android/database/sqlite/SQLiteCursor.java194
-rw-r--r--core/java/android/database/sqlite/SQLiteDatabase.java26
-rw-r--r--core/java/android/database/sqlite/SQLiteQuery.java30
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);
}