Android对数据库表有一个约定。就是每张表都应该至少有_id这列。ListView在使用CursorAdapter及其子类适配 cursor的时候,会默认的获取 _id 这列的值,如果你建的表没有 _id这列或者你的cursor中没有_id这列(查询时的projection中没有_id)就报错了。所以使用CursorAdapter及其子类的时候一定要使查询时的projection包含_id。CursorAdapter中相关代码如下:

1.注释
/**
* Adapter that exposes data from a {@link android.database.Cursor Cursor} to a
* {@link android.widget.ListView ListView} widget. The Cursor must include
* a column named "_id" or this class will not work.
*/
2.构造方法
public CursorAdapter(Context context, Cursor c) {
init(context, c, true);
}
public CursorAdapter(Context context, Cursor c, boolean autoRequery) {
init(context, c, autoRequery);
}

protected void init(Context context, Cursor c, boolean autoRequery) {
boolean cursorPresent = c != null;
mAutoRequery = autoRequery;
mCursor = c;
mDataValid = cursorPresent;
mContext = context;
mRowIDColumn = cursorPresent ? c.getColumnIndexOrThrow("_id") : -1;
mChangeObserver = new ChangeObserver();
if (cursorPresent) {
c.registerContentObserver(mChangeObserver);
c.registerDataSetObserver(mDataSetObserver);
}
}
3.public void changeCursor(Cursor cursor) {
if (cursor == mCursor) {
return;
}
if (mCursor != null) {
mCursor.unregisterContentObserver(mChangeObserver);
mCursor.unregisterDataSetObserver(mDataSetObserver);
mCursor.close();
}
mCursor = cursor;
if (cursor != null) {
cursor.registerContentObserver(mChangeObserver);
cursor.registerDataSetObserver(mDataSetObserver);
mRowIDColumn = cursor.getColumnIndexOrThrow("_id");
mDataValid = true;
// notify the observers about the new cursor
notifyDataSetChanged();

} else {
mRowIDColumn = -1;
mDataValid = false;
// notify the observers about the lack of a data set
notifyDataSetInvalidated();
}
}

其中cursor.getColumnIndexOrThrow(String columnName)如下:

1.注释
/**
* Adapter that exposes data from a {@link android.database.Cursor Cursor} to a
* {@link android.widget.ListView ListView} widget. The Cursor must include
* a column named "_id" or this class will not work.
*/
2.构造方法
public CursorAdapter(Context context, Cursor c) {
init(context, c, true);
}
public CursorAdapter(Context context, Cursor c, boolean autoRequery) {
init(context, c, autoRequery);
}

protected void init(Context context, Cursor c, boolean autoRequery) {
boolean cursorPresent = c != null;
mAutoRequery = autoRequery;
mCursor = c;
mDataValid = cursorPresent;
mContext = context;
mRowIDColumn = cursorPresent ? c.getColumnIndexOrThrow("_id") : -1;
mChangeObserver = new ChangeObserver();
if (cursorPresent) {
c.registerContentObserver(mChangeObserver);
c.registerDataSetObserver(mDataSetObserver);
}
}
3.public void changeCursor(Cursor cursor) {
if (cursor == mCursor) {
return;
}
if (mCursor != null) {
mCursor.unregisterContentObserver(mChangeObserver);
mCursor.unregisterDataSetObserver(mDataSetObserver);
mCursor.close();
}
mCursor = cursor;
if (cursor != null) {
cursor.registerContentObserver(mChangeObserver);
cursor.registerDataSetObserver(mDataSetObserver);
mRowIDColumn = cursor.getColumnIndexOrThrow("_id");
mDataValid = true;
// notify the observers about the new cursor
notifyDataSetChanged();

} else {
mRowIDColumn = -1;
mDataValid = false;
// notify the observers about the lack of a data set
notifyDataSetInvalidated();
}
}

所以只要Cursor不为空,Cursor中必须存在_id列,CursorAdapter及其子类才能正常工作。