Fields Summary |
---|
static final String | TAG |
static final int | NO_COUNT |
private String | mEditTableThe name of the table to edit |
private String[] | mColumnsThe names of the columns in the rows |
private SQLiteQuery | mQueryThe query object for the cursor |
private SQLiteDatabase | mDatabaseThe database the cursor was created from |
private SQLiteCursorDriver | mDriverThe compiled query this cursor came from |
private int | mCountThe number of rows in the cursor |
private Map | mColumnNameMapA mapping of column names to column indices, to speed up lookups |
private StackTraceElement[] | mStackTraceElementsUsed to find out where a cursor was allocated in case it never got
released. |
private int | mMaxReadmMaxRead is the max items that each cursor window reads
default to a very high value |
private int | mInitialRead |
private int | mCursorState |
private ReentrantLock | mLock |
private boolean | mPendingData |
protected MainThreadNotificationHandler | mNotificationHandler |
Methods Summary |
---|
public void | close()
super.close();
deactivateCommon();
mQuery.close();
mDriver.cursorClosed();
|
public boolean | commitUpdates(java.util.Map additionalValues)
if (!supportsUpdates()) {
Log.e(TAG, "commitUpdates not supported on this cursor, did you "
+ "include the _id column?");
return false;
}
/*
* Prevent other threads from changing the updated rows while they're
* being processed here.
*/
synchronized (mUpdatedRows) {
if (additionalValues != null) {
mUpdatedRows.putAll(additionalValues);
}
if (mUpdatedRows.size() == 0) {
return true;
}
/*
* Prevent other threads from changing the database state while
* we process the updated rows, and prevents us from changing the
* database behind the back of another thread.
*/
mDatabase.beginTransaction();
try {
StringBuilder sql = new StringBuilder(128);
// For each row that has been updated
for (Map.Entry<Long, Map<String, Object>> rowEntry :
mUpdatedRows.entrySet()) {
Map<String, Object> values = rowEntry.getValue();
Long rowIdObj = rowEntry.getKey();
if (rowIdObj == null || values == null) {
throw new IllegalStateException("null rowId or values found! rowId = "
+ rowIdObj + ", values = " + values);
}
if (values.size() == 0) {
continue;
}
long rowId = rowIdObj.longValue();
Iterator<Map.Entry<String, Object>> valuesIter =
values.entrySet().iterator();
sql.setLength(0);
sql.append("UPDATE " + mEditTable + " SET ");
// For each column value that has been updated
Object[] bindings = new Object[values.size()];
int i = 0;
while (valuesIter.hasNext()) {
Map.Entry<String, Object> entry = valuesIter.next();
sql.append(entry.getKey());
sql.append("=?");
bindings[i] = entry.getValue();
if (valuesIter.hasNext()) {
sql.append(", ");
}
i++;
}
sql.append(" WHERE " + mColumns[mRowIdColumnIndex]
+ '=" + rowId);
sql.append(';");
mDatabase.execSQL(sql.toString(), bindings);
mDatabase.rowUpdated(mEditTable, rowId);
}
mDatabase.setTransactionSuccessful();
} finally {
mDatabase.endTransaction();
}
mUpdatedRows.clear();
}
// Let any change observers know about the update
onChange(true);
return true;
|
public void | deactivate()
super.deactivate();
deactivateCommon();
mDriver.cursorDeactivated();
|
private void | deactivateCommon()
if (Config.LOGV) Log.v(TAG, "<<< Releasing cursor " + this);
mCursorState = 0;
if (mWindow != null) {
mWindow.close();
mWindow = null;
}
if (Config.LOGV) Log.v("DatabaseWindow", "closing window in release()");
|
public boolean | deleteRow()
checkPosition();
// Only allow deletes if there is an ID column, and the ID has been read from it
if (mRowIdColumnIndex == -1 || mCurrentRowID == null) {
Log.e(TAG,
"Could not delete row because either the row ID column is not available or it" +
"has not been read.");
return false;
}
boolean success;
/*
* Ensure we don't change the state of the database when another
* thread is holding the database lock. requery() and moveTo() are also
* synchronized here to make sure they get the state of the database
* immediately following the DELETE.
*/
mDatabase.lock();
try {
try {
mDatabase.delete(mEditTable, mColumns[mRowIdColumnIndex] + "=?",
new String[] {mCurrentRowID.toString()});
success = true;
} catch (SQLException e) {
success = false;
}
int pos = mPos;
requery();
/*
* Ensure proper cursor state. Note that mCurrentRowID changes
* in this call.
*/
moveToPosition(pos);
} finally {
mDatabase.unlock();
}
if (success) {
onChange(true);
return true;
} else {
return false;
}
|
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.setStartPosition(startPos);
mCount = mQuery.fillWindow(mWindow, mInitialRead, 0);
// return -1 means not finished
if (mCount == NO_COUNT){
mCount = startPos + mInitialRead;
Thread t = new Thread(new QueryThread(mCursorState), "query thread");
t.start();
}
|
protected void | finalize()Release the native resources, if they haven't been released yet.
try {
if (mWindow != null) {
close();
String message = "Finalizing cursor " + this + " on " + mEditTable
+ " that has not been deactivated or closed";
if (SQLiteDebug.DEBUG_ACTIVE_CURSOR_FINALIZATION) {
Log.d(TAG, message + "\nThis cursor was created in:");
for (StackTraceElement ste : mStackTraceElements) {
Log.d(TAG, " " + ste);
}
}
SQLiteDebug.notifyActiveCursorFinalized();
throw new IllegalStateException(message);
} else {
if (Config.LOGV) {
Log.v(TAG, "Finalizing cursor " + this + " on " + mEditTable);
}
}
} finally {
super.finalize();
}
|
public int | getColumnIndex(java.lang.String columnName)
// Create mColumnNameMap on demand
if (mColumnNameMap == null) {
String[] columns = mColumns;
int columnCount = columns.length;
HashMap<String, Integer> map = new HashMap<String, Integer>(columnCount, 1);
for (int i = 0; i < columnCount; i++) {
map.put(columns[i], i);
}
mColumnNameMap = map;
}
// 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);
}
Integer i = mColumnNameMap.get(columnName);
if (i != null) {
return i.intValue();
} else {
return -1;
}
|
public java.lang.String[] | getColumnNames()
return mColumns;
|
public int | getCount()
if (mCount == NO_COUNT) {
fillWindow(0);
}
return mCount;
|
public SQLiteDatabase | getDatabase()
return mDatabase;
|
public boolean | onMove(int oldPosition, int newPosition)
// Make sure the row at newPosition is present in the window
if (mWindow == null || newPosition < mWindow.getStartPosition() ||
newPosition >= (mWindow.getStartPosition() + mWindow.getNumRows())) {
fillWindow(newPosition);
}
return true;
|
private void | queryThreadLock()
if (mLock != null) {
mLock.lock();
}
|
private void | queryThreadUnlock()
if (mLock != null) {
mLock.unlock();
}
|
public void | registerDataSetObserver(android.database.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();
}
}
|
public boolean | requery()
if (isClosed()) {
return false;
}
long timeStart = 0;
if (Config.LOGV) {
timeStart = System.currentTimeMillis();
}
/*
* Synchronize on the database lock to ensure that mCount matches the
* results of mQuery.requery().
*/
mDatabase.lock();
try {
if (mWindow != null) {
mWindow.clear();
}
mPos = -1;
// This one will recreate the temp table, and get its count
mDriver.cursorRequeried(this);
mCount = NO_COUNT;
mCursorState++;
queryThreadLock();
try {
mQuery.requery();
} finally {
queryThreadUnlock();
}
} finally {
mDatabase.unlock();
}
if (Config.LOGV) {
Log.v("DatabaseWindow", "closing window in requery()");
Log.v(TAG, "--- Requery()ed cursor " + this + ": " + mQuery);
}
boolean result = super.requery();
if (Config.LOGV) {
long timeEnd = System.currentTimeMillis();
Log.v(TAG, "requery (" + (timeEnd - timeStart) + " ms): " + mDriver.toString());
}
return result;
|
public void | setLoadStyle(int initialRead, int maxRead)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
mMaxRead = maxRead;
mInitialRead = initialRead;
mLock = new ReentrantLock(true);
|
public void | setSelectionArguments(java.lang.String[] selectionArgs)Changes the selection arguments. The new values take effect after a call to requery().
mDriver.setBindArguments(selectionArgs);
|
public void | setWindow(android.database.CursorWindow window)
if (mWindow != null) {
mCursorState++;
queryThreadLock();
try {
mWindow.close();
} finally {
queryThreadUnlock();
}
mCount = NO_COUNT;
}
mWindow = window;
|
public boolean | supportsUpdates()
return super.supportsUpdates() && !TextUtils.isEmpty(mEditTable);
|