Cursor是Android下的游标接口,封装了大部分的游标操作。其操作主要可以分为三类:
一、Move操作。获取一个游标后,其内部结构如下:
before first | first | middle positon | last | after last | ||
-1 | 0 | 1 | … | count - 2 | count - 1 | count |
了解了上述结构之后,下面的接口就容易理解了
//获取游标里的行数,即count
int getCount();
//获取游标的当前位置
int getPosition();
//在当前位置的基础上,移动游标,可前进或后退
boolean move(int offset);
//移动游标到一个绝对位置
boolean moveToPosition(int position);
//移动到第一行,即first
boolean moveToFirst();
//移动到最后一行,即last
boolean moveToLast();
//移动到下一行
boolean moveToNext();
//移动到前一行
boolean moveToPrevious();
//是否是第一行
boolean isFirst();
//是否是最后一行
boolean isLast();
//是否是第一行的前一行,即before first
boolean isBeforeFirst();
//是否是最后一行的后一行,即after last
boolean isAfterLast();
这部分的接口主要是在AbstractCursor类里面实现,主要是moveToPosition接口,源码如下:
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;
}
首先是进行参数合法性检验,如果合法,则移动游标,并将position设为当前位置。
其他参数都是moveToPosition接口的一个Wrapper,源码如下:
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();
}
二、Get操作。从游标中获取当前行的各个字段值。主要接口如下:
int getColumnIndex(String columnName);
int getColumnIndexOrThrow(String columnName) throws IllegalArgumentException;
String[] getColumnNames();
int getColumnCount();
byte[] getBlob(int columnIndex);
String getString(int columnIndex);
short getShort(int columnIndex);
int getInt(int columnIndex);
long getLong(int columnIndex);
float getFloat(int columnIndex);
double getDouble(int columnIndex);
boolean isNull(int columnIndex);
获取Index的两个接口主要是在Abstr actCursor里面实现,源码如下:
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 (Config.LOGV) {
if (getCount() > 0) {
Log.w("AbstractCursor", "Unknown column " + columnName);
}
}
return -1;
}
首先检查该字段名是否包含表名,即tableName.columnName结构,如果有则提取出字段名。然后在所有字段名里面遍历查找columnName,如果找到,则返回其索引,否则返回-1.从equalsIgnoreCase可以看出,其字段是不区分大小写的。
public int getColumnIndexOrThrow(String columnName) {
final int index = getColumnIndex(columnName);
if (index < 0) {
throw new IllegalArgumentException("column '" + columnName + "' does not exist");
}
return index;
}
其他的获取接口主要是在AbstractWindowedCursor类里面实现,如getString:
public String getString(int columnIndex)
{
checkPosition();
synchronized(mUpdatedRows) {
if (isFieldUpdated(columnIndex)) {
return (String)getUpdatedField(columnIndex);
}
}
return mWindow.getString(mPos, columnIndex);
}
首先是检查在判断要获取的字段是否被更新过,即该字段是否在mUpdatedRows里面。如果在mUpdatedRows里面找到了所需字段,则将其返回。mUpdatedRows是在AbstractCursor里面定义的:
/**
* This HashMap contains a mapping from Long rowIDs to another Map
* that maps from String column names to new values. A NULL value means to
* remove an existing value, and all numeric values are in their class
* forms, i.e. Integer, Long, Float, etc.
*/
protected HashMap<Long, Map<String, Object>> mUpdatedRows;
可见,其底层的数据结果是两层嵌套的Map,第一层的索引为行ID,第二层的索引为字段名。
isFieldUpdated和getUpdatedField接口定义如下:
/**
* This function returns true if the field has been updated and is
* used in conjunction with {@link #getUpdatedField} to allow subclasses to
* support reading uncommitted updates. NOTE: This function and
* {@link #getUpdatedField} should be called together inside of a
* block synchronized on mUpdatedRows.
*
* @param columnIndex the column index of the field to check
* @return true if the field has been updated, false otherwise
*/
protected boolean isFieldUpdated(int columnIndex) {
if (mRowIdColumnIndex != -1 && mUpdatedRows.size() > 0) {
Map<String, Object> updates = mUpdatedRows.get(mCurrentRowID);
if (updates != null && updates.containsKey(getColumnNames()[columnIndex])) {
return true;
}
}
return false;
}
/**
* This function returns the uncommitted updated value for the field
* at columnIndex. NOTE: This function and {@link #isFieldUpdated} should
* be called together inside of a block synchronized on mUpdatedRows.
*
* @param columnIndex the column index of the field to retrieve
* @return the updated value
*/
protected Object getUpdatedField(int columnIndex) {
Map<String, Object> updates = mUpdatedRows.get(mCurrentRowID);
return updates.get(getColumnNames()[columnIndex]);
}
都是比较简单的Map操作。
返回到getString,如果该
字段没有更新过,则从CursorWindow里面获取该字段,即:
mWindow.getString(mPos, columnIndex);
mWindow是一个CursorWindow类型。CursorWindow是对Cursor操作的最低层的封装,其内部调用了native函数。如上面的getString接口:
public String getString(int row, int col) {
acquireReference();
try {
return getString_native(row - mStartPos, col);
} finally {
releaseReference();
}
}
private native String getString_native(int row, int col);
首先是进行引用计数,然后调用native函数来获取字段值。
其他Get接口与此类似。
三、状态操作。改变Cursor的状态,有如下几个接口:
void deactivate();
boolean requery();
void close();
boolean isClosed();
void registerContentObserver(ContentObserver observer);
void unregisterContentObserver(ContentObserver observer);
void registerDataSetObserver(DataSetObserver observer);
void unregisterDataSetObserver(DataSetObserver observer);
deactivate接口使Cursor无效,其内部会调用DataSetObserver的notifyInvalidated接口。调用requery接口使Cursor重新变得有效。
close接口关闭当前的Cursor,即使调用requery接口也不会使Cursor重新打开。
最后四个接口是注册观察者的,当数据有改变,或无效时通知观察者。