I am getting a android.database.CursorIndexOutOfBoundsException: Index 0 requested, with a size of 0 error when trying list the data from an Sqlite database. From reading other issues with the same error I can tell that there is a null value for the first record. The resolution seems to be to close off the cursor, but I am already doing this, so this cannot be the cause. Unless I am closing off the cursor incorrectly. I have post the error I am getting and the code that I believe is associated to the error. Any guidance on how to resolve this error would be appreciated.
Process: ca.rvogl.tpbcui, PID: 30793
android.database.CursorIndexOutOfBoundsException: Index 0 requested, with a size of 0
at android.database.AbstractCursor.checkPosition(AbstractCursor.java:460)
at android.database.AbstractWindowedCursor.checkPosition(AbstractWindowedCursor.java:136)
at android.database.AbstractWindowedCursor.getInt(AbstractWindowedCursor.java:68)
at ca.rvogl.tpbcui.Database.DatabaseHelper.getGame(DatabaseHelper.java:445)
at ca.rvogl.tpbcui.Views.GameActivity.createGame(GameActivity.java:113)
at ca.rvogl.tpbcui.Views.GameActivity.access$800(GameActivity.java:30)
at ca.rvogl.tpbcui.Views.GameActivity$7.onClick(GameActivity.java:244)
at android.view.View.performClick(View.java:5637)
at android.view.View$PerformClick.run(View.java:22429)
at android.os.Handler.handleCallback(Handler.java:751)
at android.os.Handler.dispatchMessage(Handler.java:95)
at android.os.Looper.loop(Looper.java:154)
at android.app.ActivityThread.main(ActivityThread.java:6119)
at java.lang.reflect.Method.invoke(Native Method)
at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:886)
at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:776)
Code from my DatabaseHelper
public Game getGame(String leagueId, String bowlerId, String seriesId) {
//Get Readable Database If We Are Not Inserting Anything
SQLiteDatabase db = this.getReadableDatabase();
Cursor cursor = db.query( Game.TABLE_NAME,
new String[]{Game.COLUMN_ID, Game.COLUMN_LEAGUE_ID, Game.COLUMN_BOWLER_ID,
Game.COLUMN_SERIES_ID, Game.COLUMN_SCORE, Game.COLUMN_STRIKES, Game.COLUMN_SPARES,Game.COLUMN_SPLITS, Game.COLUMN_SPLIT_CONVERSIONS, Game.COLUMN_OPEN_FRAMES, Game.COLUMN_TIMESTAMP},
Game.COLUMN_LEAGUE_ID + "=?" + " AND " + Game.COLUMN_BOWLER_ID + "=?" + " AND " + Game.COLUMN_SERIES_ID + "=?",
new String[]{String.valueOf(leagueId), String.valueOf(bowlerId), String.valueOf(seriesId)}, null, null, null, null);
if (cursor != null)
cursor.moveToFirst();
//Prepare Game Object
Game game = new Game(
cursor.getInt(cursor.getColumnIndex(Game.COLUMN_ID)),
cursor.getString(cursor.getColumnIndex(Game.COLUMN_LEAGUE_ID)),
cursor.getString(cursor.getColumnIndex(Game.COLUMN_BOWLER_ID)),
cursor.getString(cursor.getColumnIndex(Game.COLUMN_SERIES_ID)),
cursor.getString(cursor.getColumnIndex(Game.COLUMN_SCORE)),
cursor.getString(cursor.getColumnIndex(Game.COLUMN_STRIKES)),
cursor.getString(cursor.getColumnIndex(Game.COLUMN_SPARES)),
cursor.getString(cursor.getColumnIndex(Game.COLUMN_SPLITS)),
cursor.getString(cursor.getColumnIndex(Game.COLUMN_SPLIT_CONVERSIONS)),
cursor.getString(cursor.getColumnIndex(Game.COLUMN_OPEN_FRAMES)),
cursor.getString(cursor.getColumnIndex(Game.COLUMN_TIMESTAMP)));
//Close Database Connection
cursor.close();
return game;
}
public List<Game> getAllGames(String leagueId, String bowlerId, String seriesId) {
List<Game> games = new ArrayList<>();
//Select All Query
String selectQuery = "SELECT * FROM " + Game.TABLE_NAME + " WHERE " + Game.COLUMN_LEAGUE_ID + " = '" + leagueId + "'" + " AND " + Game.COLUMN_BOWLER_ID + " = '" + bowlerId + "'" + " AND " + Game.COLUMN_SERIES_ID + " = '" + seriesId + "'" + " ORDER BY " +
Game.COLUMN_TIMESTAMP + " DESC";
try (SQLiteDatabase db = this.getWritableDatabase()) {
@SuppressLint("Recycle") Cursor cursor = db.rawQuery( selectQuery, null );
//Looping Through All Rows And Adding To The List
if (cursor.moveToFirst()) {
do {
Game game1 = new Game();
game1.setId(cursor.getInt(cursor.getColumnIndex(Game.COLUMN_ID ) ) );
game1.setLeagueId(cursor.getString(cursor.getColumnIndex(Game.COLUMN_LEAGUE_ID)));
game1.setBowlerId(cursor.getString(cursor.getColumnIndex(Game.COLUMN_BOWLER_ID)));
game1.setSeriesId(cursor.getString(cursor.getColumnIndex(Game.COLUMN_SERIES_ID)));
game1.setScore(cursor.getString(cursor.getColumnIndex(Game.COLUMN_SCORE)));
game1.setSpares(cursor.getString(cursor.getColumnIndex(Game.COLUMN_SPARES)));
game1.setSplits(cursor.getString(cursor.getColumnIndex(Game.COLUMN_SPLITS)));
game1.setSplitConversions(cursor.getString(cursor.getColumnIndex(Game.COLUMN_SPLIT_CONVERSIONS)));
game1.setOpenFrames(cursor.getString(cursor.getColumnIndex(Game.COLUMN_OPEN_FRAMES)));
game1.setTimestamp(cursor.getString(cursor.getColumnIndex(Game.COLUMN_TIMESTAMP)));
games.add( game1 );
} while (cursor.moveToNext());
}
//Close Database Connection
db.close();
}
//Return Game List
return games;
}
Code from Games.java
//Inserting New Game In The Database & Refreshing The List
private void createGame(String leagueId, String bowlerId, String seriesId, String score, String spares, String strikes, String splits, String splitConversions, String openFrames) {
//Inserting Game In The Database & Getting Newly Inserted Game Id
long id = db.insertGame(leagueId, bowlerId, seriesId, score, strikes, spares, splits, splitConversions, openFrames);
//Getting The Newly Inserted Game From The Database
Game n = db.getGame(leagueId, bowlerId, seriesId);
if (n != null) {
//Adding New Game To The Array at Position 0
gameList.add(0, n);
//Refreshing The List
mAdapter.notifyDataSetChanged();
toggleEmptyGames();
}
}
//Updating Game In The Database & Updating Item In The List By Its Position
private void updateGame(String score, String strikes, String spares, String splits, String splitConversions, String openFrames, int position) {
Game n = gameList.get(position);
//Updating the Game Text
n.setScore(score);
n.setStrikes(strikes);
n.setSpares(spares);
n.setSplits(splits);
n.setSplitConversions(splitConversions);
n.setOpenFrames(openFrames);
//Updating Game In The Database
db.updateGame(n);
//Refreshing The List
gameList.set(position, n);
mAdapter.notifyItemChanged(position);
toggleEmptyGames();
}
//Deleting Game From SQWLite & Removing The Item From The List By Its Position
private void deleteGame(int position) {
//Deleting Game From The Database
db.deleteGame(gameList.get(position));
//Removing Game From The List
gameList.remove(position);
mAdapter.notifyItemRemoved(position);
toggleEmptyGames();
}
//Opens Dialog With Edit - Delete Options
private void showActionsDialog(final int position) {
CharSequence colors[] = new CharSequence[]{"Edit", "Delete"};
AlertDialog.Builder builder = new AlertDialog.Builder(this);
builder.setTitle("Choose option");
builder.setItems(colors, new DialogInterface.OnClickListener() {
@Override
public void onClick(DialogInterface dialog, int which) {
if (which == 0) {
showGameDialog(true, gameList.get(position), position);
} else {
deleteGame(position);
}
}
});
builder.show();
}
This error is only appearing when I attempt to add another game to the database. When I restart the application the data that was enter appears in the listview, it is however not correct there are missing values from some of the fields.
Revised Code
public Game getGame(String leagueId, String bowlerId, String seriesId) {
//Get Readable Database If We Are Not Inserting Anything
SQLiteDatabase db = this.getReadableDatabase();
Cursor cursor = db.query( Game.TABLE_NAME,
new String[]{Game.COLUMN_ID, Game.COLUMN_LEAGUE_ID, Game.COLUMN_BOWLER_ID,
Game.COLUMN_SERIES_ID, Game.COLUMN_SCORE, Game.COLUMN_STRIKES, Game.COLUMN_SPARES, Game.COLUMN_SPLITS, Game.COLUMN_SPLIT_CONVERSIONS, Game.COLUMN_OPEN_FRAMES, Game.COLUMN_TIMESTAMP},
Game.COLUMN_LEAGUE_ID + "=?" + " AND " + Game.COLUMN_BOWLER_ID + "=?" + " AND " + Game.COLUMN_SERIES_ID + "=?",
new String[]{String.valueOf( leagueId ), String.valueOf( bowlerId ), String.valueOf( seriesId )}, null, null, null, null );
// if (cursor.getCount()>0)
// cursor.moveToFirst();
// Log.d("Events",Integer.toString(cursor.getCount()));
if (cursor.moveToFirst()) {
//Prepare Game Object
Game game = new Game(
cursor.getInt( cursor.getColumnIndex( Game.COLUMN_ID ) ),
cursor.getString( cursor.getColumnIndex( Game.COLUMN_LEAGUE_ID ) ),
cursor.getString( cursor.getColumnIndex( Game.COLUMN_BOWLER_ID ) ),
cursor.getString( cursor.getColumnIndex( Game.COLUMN_SERIES_ID ) ),
cursor.getString( cursor.getColumnIndex( Game.COLUMN_SCORE ) ),
cursor.getString( cursor.getColumnIndex( Game.COLUMN_STRIKES ) ),
cursor.getString( cursor.getColumnIndex( Game.COLUMN_SPARES ) ),
cursor.getString( cursor.getColumnIndex( Game.COLUMN_SPLITS ) ),
cursor.getString( cursor.getColumnIndex( Game.COLUMN_SPLIT_CONVERSIONS ) ),
cursor.getString( cursor.getColumnIndex( Game.COLUMN_OPEN_FRAMES ) ),
cursor.getString( cursor.getColumnIndex( Game.COLUMN_TIMESTAMP ) ) );
//Close Database Connection
cursor.close();
return game;
} else {
}
}
The reason you are getting this error is two-fold. First there are no rows being returned by the query which is then not properly checked/handled as you are checking for a null Cursor when a Cursor returned from the query
method will not be null. It may be empty (cursor.getCount() would return 0).
All of the Cursor move????
methods return true or false depending on whether or not the move can be made.
As such to correctly handle reading a cursor you should never use (it is useless and as in your case disruptive) :-
if (cursor != null) {
cursor.moveToFirst(); // (or any move???? method
..... Ouch if the Cursor is empty as there will be no first row to extract data from
}
rather you should use :-
if (cursor.moveToFirst()) {
..... do your stuff here
} else {
..... handle no row found here if wanted
}
For looping though a Cursor you can use :-
while (cursor.moveToNext()) {
.... handle each row here
}
If you need to loop through a cursor and handle no rows in the cursor you can use :-
if (cursor.getCount() < 1) {
..... handle no rows here
}
while (cursor.moveToNext()) {
..... handle each row here
}
Noting that in every situation you should close the Cursor when done with it.