Search code examples
javascriptandroidsqliteandroid-cursorcursor-position

Index 0 Requested, With Size 0


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 {
        }
    }

Solution

  • 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.