Why might Cursor.getLong() and Cursor.getString() work correctly on an emulator but not an actual device running the same version of Android?
I made some additions to version three of Android’s Notepad tutorial that involve programmatically changing notes’ titles. (I recently asked about this project at Obtaining the current Android view and forcing it to be redrawn.) My changes work fine in my emulator, but cause an app-ending “Unfortunately, program has stopped” on my testing phone.
Here’s the method where this is happening:
public void expandNoteTitle(int i) {
Cursor note = fetchNote(i);
long rowId =
note.getLong(note.getColumnIndexOrThrow(NotesDbAdapter.KEY_ROWID));
String title =
note.getString(note.getColumnIndexOrThrow(NotesDbAdapter.KEY_TITLE)) + "W";
String body =
note.getString(note.getColumnIndexOrThrow(NotesDbAdapter.KEY_BODY));
updateNote(rowId, title, body);
}
I know that fetching the cursor is at least partially working because note.getColumnName(1) works fine in the debugger. I also confirmed that the arguments to getLong() and getString() show up correctly.
Here’s the LogCat output:
08-12 15:35:29.735: D/AndroidRuntime(17937): Shutting down VM
08-12 15:35:29.735: W/dalvikvm(17937): threadid=1: thread exiting with uncaught exception (group=0x40a3d1f8)
08-12 15:35:29.751: E/AndroidRuntime(17937): FATAL EXCEPTION: main
08-12 15:35:29.751: E/AndroidRuntime(17937): android.database.CursorIndexOutOfBoundsException: Index 0 requested, with a size of 0
08-12 15:35:29.751: E/AndroidRuntime(17937): at android.database.AbstractCursor.checkPosition(AbstractCursor.java:400)
08-12 15:35:29.751: E/AndroidRuntime(17937): at android.database.AbstractWindowedCursor.checkPosition(AbstractWindowedCursor.java:136)
08-12 15:35:29.751: E/AndroidRuntime(17937): at android.database.AbstractWindowedCursor.getLong(AbstractWindowedCursor.java:74)
08-12 15:35:29.751: E/AndroidRuntime(17937): at notepad.NotesDbAdapter.expandNoteTitle(NotesDbAdapter.java:212)
08-12 15:35:29.751: E/AndroidRuntime(17937): at notepad.NotesDbAdapter.expandNoteTitles(NotesDbAdapter.java:201)
08-12 15:35:29.751: E/AndroidRuntime(17937): at notepad.NotepadMain.expandTitles(NotepadMain.java:167)
08-12 15:35:29.751: E/AndroidRuntime(17937): at notepad.NotepadMain.onMenuItemSelected(NotepadMain.java:115)
08-12 15:35:29.751: E/AndroidRuntime(17937): at com.android.internal.policy.impl.PhoneWindow.onMenuItemSelected(PhoneWindow.java:950)
08-12 15:35:29.751: E/AndroidRuntime(17937): at [SNIPPED FOR LENGTH]
08-12 15:35:29.751: E/AndroidRuntime(17937): at dalvik.system.NativeStart.main(Native Method)
08-12 15:36:49.993: I/Process(17937): Sending signal. PID: 17937 SIG: 9
And here’s the code for fetchNote(). It is the same as the version given in the tutorial except for a variable name.
public Cursor fetchNote(long rowId) throws SQLException {
Cursor mCursor = database.query(true, DATABASE_TABLE, new String[] {
KEY_ROWID, KEY_TITLE, KEY_BODY }, KEY_ROWID + "=" + rowId, null,
null, null, null, null);
if(mCursor != null) {
mCursor.moveToFirst();
}
return mCursor;
}
Cursor’s getLong() and getString() methods work identically on both emulators and actual devices.
If you look at your LogCat, you’ll see the cause of your problem is that there is no data:
It appears that the Cursor is empty, does your table (on the device) have any rows?
Added from comments
As we discovered you made the assumption that all of your row id’s were sequential, but that is not true. Here are my two suggestions to fetch the right data and prevent any future force closes from your Cursors:
Fetch all of the existing row ids and iterate through them:
To iterate through a new Cursor you only to check the return value from moveToNext(). When the Cursor’s index is set to
-1(like when it’s new), moveToNext() will move to index0and returntrueonly if there is valid data, otherwise if the are no rows moveToNext() returnsfalse.Always assume that a Cursor could be empty and understand that an empty cursor does not equal
null: