Search code examples
androidsqlitehttp-postandroid-volleyandroid-cursor

Android Studio SQLite, Index Cursor Out Of Bounds Exception


I'm trying to develop an app, which stores data locally on an SQLite database, which can later be transferred to an online database when the tablet running the app has a WiFi connection. The local storage works fine - every time I add a record, it gets stored without issue. The connection to the online database works without issue - if I send it blank data, it enters a blank record. But, when I try to get the data from the SQLite, and send that data, I get an index cursor out of bounds exception, and I can't see why.

My Java code is as follows:

public void send_onClick(View v) {
    int numRecords = data.getCount();
    final TextView records = (TextView) findViewById(R.id.lblRecords);
    final Button send = (Button) findViewById(R.id.btnSend);

    send.setClickable(false);
    // Loop through the records and send them
    while (data.moveToNext()) {

        // Load the string request
        StringRequest stringRequest = new StringRequest(Request.Method.POST, serverUrl,
                new Response.Listener<String>() {
                    @Override
                    public void onResponse(String response) {
                        if (!response.equals("false")) success = true;
                        else Toast.makeText(SendDataActivity.this, "False response.", Toast.LENGTH_SHORT).show();
                    }
                },
                new Response.ErrorListener() {
                    @Override
                    public void onErrorResponse(VolleyError error) {
                        Toast.makeText(SendDataActivity.this, "An unknown error occurred.", Toast.LENGTH_SHORT).show();
                        error.printStackTrace();
                    }
                }){

            @Override
            protected Map<String, String> getParams() throws AuthFailureError {
                Map<String, String> params = new HashMap<String, String>();
                params.put("key","###");
                params.put("a", data.getString(1));
                params.put("b", data.getString(2));
                params.put("c", data.getString(3));
                params.put("d", data.getString(4));
                params.put("e", data.getString(5));
                params.put("f", data.getString(6));
                params.put("g", data.getString(7));
                params.put("h", data.getString(8));
                params.put("i", data.getString(9));
                params.put("j", data.getString(10));
                return params;
            }
        };

        RequestHandler.getInstance(SendDataActivity.this).addToRequestQueue(stringRequest);

        // Remove the record from the SQLite if it worked
        if (success) {
            dbHelper.deleteRecord(data.getString(0));
            numRecords--;
            records.setText(Integer.toString(numRecords));
            success = false;
        }

    }

    send.setClickable(true);

    // If there are no records left, hide the button
    if (numRecords == 0) send.setVisibility(View.GONE);
}

So as you can see, when the user clicks "send", it should loop through each record in the SQLite and send an HTTP POST request. I know the post request works, because if you replace the params with blanks, it is added to the database. The issue seems to lie with the data.getString(1) and so on that I am trying to put in the map.

E/Volley: [181] NetworkDispatcher.processRequest: Unhandled exception android.database.CursorIndexOutOfBoundsException: Index 1 requested, with a size of 1
      android.database.CursorIndexOutOfBoundsException: Index 1 requested, with a size of 1
          at android.database.AbstractCursor.checkPosition(AbstractCursor.java:460)
          at android.database.AbstractWindowedCursor.checkPosition(AbstractWindowedCursor.java:136)
          at android.database.AbstractWindowedCursor.getString(AbstractWindowedCursor.java:50)
          at com.lensofamoose.testapp.SendDataActivity$4$override.getParams(SendDataActivity.java:118)
          at com.lensofamoose.testapp.SendDataActivity$4$override.access$dispatch(SendDataActivity.java)
          at com.lensofamoose.testapp.SendDataActivity$4.getParams(SendDataActivity.java:0)
          at com.android.volley.Request.getBody(Request.java:464)
          at com.android.volley.toolbox.HurlStack.addBodyIfExists(HurlStack.java:275)
          at com.android.volley.toolbox.HurlStack.setConnectionParametersForRequest(HurlStack.java:249)
          at com.android.volley.toolbox.HurlStack.executeRequest(HurlStack.java:94)
          at com.android.volley.toolbox.BasicNetwork.performRequest(BasicNetwork.java:123)
          at com.android.volley.NetworkDispatcher.processRequest(NetworkDispatcher.java:131)
          at com.android.volley.NetworkDispatcher.processRequest(NetworkDispatcher.java:111)
          at com.android.volley.NetworkDispatcher.run(NetworkDispatcher.java:90)

You can see here the error that I get is telling me that I have a Cursor out of bounds exception. If there is only one record, then it's index 1, size 1. If there are two records, then I get index 2, size 2, etc. "SendDataActivity.java:118" is the first line where I try to use data.getString().

I've tried every method I can think of for the loop. I've tried putting moveToFirst() followed by do-while. I've tried checking isAfterLast() and returning if that is true. Nothing seems to do the trick - I can't see a logical reason why it's trying to get an out of bounds index, because even when I change the loop to try and take that into account, I still seem to get the same error. Any ideas?

EDIT:

The data is extracted as follows:

public Cursor getData() {
    SQLiteDatabase db = this.getWritableDatabase();
    Cursor data = db.rawQuery("SELECT * FROM " + TABLE_NAME, null);
    return data;
}

And this method is passed into the data variable with the following:

data = dbHelper.getData();

Solution

  • After toying with it for hours, it turns out the scope in which I am trying to access the data was the problem. If, at the start of the while loop, I store the necessary data in a final variable, and call those from within params.map() instead of data.getString(), I get no errors. So instead, I get this:

    while (data.moveToNext()) {
    
    final String a = data.getString(1);
                final String b = data.getString(2);
                final String c = data.getString(3);
                final String d = data.getString(4);
                final String e = data.getString(5);
                final String f = data.getString(6);
                final String g = data.getString(7);
                final String h = data.getString(8);
                final String i = data.getString(9);
                final String j = data.getString(10);
    
        // Load the string request
        StringRequest stringRequest = new StringRequest(Request.Method.POST, serverUrl,
                new Response.Listener<String>() {
                    @Override
                    public void onResponse(String response) {
                        if (!response.equals("false")) success = true;
                        else Toast.makeText(SendDataActivity.this, "False response.", Toast.LENGTH_SHORT).show();
                    }
                },
                new Response.ErrorListener() {
                    @Override
                    public void onErrorResponse(VolleyError error) {
                        Toast.makeText(SendDataActivity.this, "An unknown error occurred.", Toast.LENGTH_SHORT).show();
                        error.printStackTrace();
                    }
                }){
    
            @Override
            protected Map<String, String> getParams() throws AuthFailureError {
                Map<String, String> params = new HashMap<String, String>();
                params.put("key","###");
                params.put("a", a);
                params.put("b", b);
                params.put("c", c);
                params.put("d", d);
                params.put("e", e);
                params.put("f", f);
                params.put("g", g);
                params.put("h", h);
                params.put("i", i);
                params.put("j", j);
                return params;
            }
        };
    
        RequestHandler.getInstance(SendDataActivity.this).addToRequestQueue(stringRequest);
    
        // Remove the record from the SQLite if it worked
        if (success) {
            dbHelper.deleteRecord(data.getString(0));
            numRecords--;
            records.setText(Integer.toString(numRecords));
            success = false;
        }
    
    }