Android源码之Cursor

Cursor是Android下的游标接口,封装了大部分的游标操作。其操作主要可以分为三类:

一、Move操作。获取一个游标后,其内部结构如下:

before firstfirstmiddle positonlastafter last
-101count - 2count - 1count

了解了上述结构之后,下面的接口就容易理解了

//获取游标里的行数,即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重新打开。
最后四个接口是注册观察者的,当数据有改变,或无效时通知观察者。





评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值