kenyu73
kenyu73

Reputation: 681

ArrayIndexOutOfBoundsException error... why?

I'm a bit lost on this one. How can I get an OutOfBounds? Is there a size limit (besides sizeof (int))?

Maybe because multiple threads can come here? The UI thread and a Service thread?

java.lang.ArrayIndexOutOfBoundsException at kenyu73.realsignal.DatabaseWrapper.getSignalValues(DatabaseWrapper.java:137) at kenyu73.realsignal.DatabaseWrapper.getSignalValues(DatabaseWrapper.java:116) at kenyu73.realsignal.BarScaleGraph$buildGraphThread.drawGraph(BarScaleGraph.java:128) at kenyu73.realsignal.BarScaleGraph$buildGraphThread.execute(BarScaleGraph.java:94) at kenyu73.realsignal.BarScaleGraph$buildGraphThread.run(BarScaleGraph.java:74)

Also, I'm calling this classes methods with a static instance. I'm thinking threads are competing for the same variables??? Thoughts?

BarScaleGraph class

ContentValues[] values = DatabaseWrapper.getInstance().getSignalValues(getContentResolver(), signal_type, false);

DatabaseWrapper class

private static final DatabaseWrapper    instance    = new DatabaseWrapper();

// grab static instance so we only have one db wrapper
public static DatabaseWrapper getInstance() {
    return instance;
}

. . . .

public ContentValues[] getSignalValues(ContentResolver cr, int signal_type_id, boolean bGroupByLatLon) {

    String sWhere = "signal_type_id=" + signal_type_id;

    Cursor cursor;

    if (bGroupByLatLon) {
        cursor = cr.query(CONSTS.CONTENT_URI_GRP_LATLNG, null, sWhere, null, null);
    } else {
        cursor = cr.query(CONSTS.CONTENT_URI_LOGGER, null, sWhere, null, null);
    }

    ContentValues[] values = new ContentValues[cursor.getCount()];

    int count = 0;
    if (cursor.getCount() > 0) {
        cursor.moveToFirst();
        do {
            values[count] = new ContentValues(); // <--- LINE 137
            values[count].put("signal_value", cursor.getInt(cursor.getColumnIndex("signal_value")));
            values[count].put("latitude", cursor.getInt(cursor.getColumnIndex("latitude")));
            values[count].put("longitude", cursor.getInt(cursor.getColumnIndex("longitude")));
            values[count].put("timestamp", cursor.getLong(cursor.getColumnIndex("timestamp")));
            values[count].put("network", cursor.getString(cursor.getColumnIndex("network")));

            count++;

        } while (cursor.moveToNext());
    }
    cursor.close();

    return values;
}

EDIT: Going to try this - add synchronized to the instance

// grab static instance so we only have one db wrapper
public static synchronized DatabaseWrapper getInstance() {
    return instance;
}

Upvotes: 1

Views: 327

Answers (6)

TacB0sS
TacB0sS

Reputation: 10266

I would guess this is a race condition, where the count is change between threads... try to synchronize the method:

public synchronized ContentValues[] getSignalValues(...){
    ...
}

If the previous does not fit you, there is always this:

public ContentValues[] method1(...){
    synchronized (monitor1) {
        ...
    }
}

public ContentValues[] method2(...){
    synchronized (monitor2) {
        ...
    }
}

This would solve the issue, but I would try to prevented this sort of methodology using different architecture.

Upvotes: 1

T.J. Crowder
T.J. Crowder

Reputation: 1073978

The only reasonable answer is that cursor.getCount() is returning a number lower than the number of loops that your do..while loop makes. I'm not seeing an error in the logic of your do..while loop (though it's unusual logic; see below).

My guess would be that it's a live cursor, and something else is deleting adding relevant rows while your loop is running. The only real way to find out will be to add instrumentation to the code so you can see what cursor.getCount() returned, what count was at the beginning of each loop iteration, etc.

If you don't really care why and just want it to stop happening, you could use a List instead:

public ContentValues[] getSignalValues(ContentResolver cr, int signal_type_id, boolean bGroupByLatLon) {

    String sWhere = "signal_type_id=" + signal_type_id;

    Cursor cursor;

    if (bGroupByLatLon) {
        cursor = cr.query(CONSTS.CONTENT_URI_GRP_LATLNG, null, sWhere, null, null);
    } else {
        cursor = cr.query(CONSTS.CONTENT_URI_LOGGER, null, sWhere, null, null);
    }

    List<ContentValues> values = new LinkedList<ContentValues>();
    ContentValues entry;

    while (cursor.moveToNext()) {
        entry = new ContentValues();
        entry.put("signal_value", cursor.getInt(cursor.getColumnIndex("signal_value")));
        entry.put("latitude", cursor.getInt(cursor.getColumnIndex("latitude")));
        entry.put("longitude", cursor.getInt(cursor.getColumnIndex("longitude")));
        entry.put("timestamp", cursor.getLong(cursor.getColumnIndex("timestamp")));
        entry.put("network", cursor.getString(cursor.getColumnIndex("network")));
        values.add(entry);
    }
    cursor.close();

    return values.toArray(new ContentValues[values.size()]);
}

(Or code to that effect.)

There I've used a temporary linked list so I don't care what cursor.getCount returns, converting it into an array when we're done. I've also used the more common idiom for looping cursors (since cursors start out just before the first row, while (cursor.moveToNext()) is a handy way to loop), not (again) that I saw a logic fault in your do..while, but I like the simplicity and directness of while (cursor.moveToNext()).

Upvotes: 2

user370305
user370305

Reputation: 109237

What about this,

int count = 0;
if (cursor.getCount() > 0) {
ContentValues[] values = new ContentValues[cursor.getCount()];
cursor.moveToFirst();
do {
        if(cursor.getCount() >= count)
        {
        values[count].put("signal_value", cursor.getInt(cursor.getColumnIndex("signal_value")));
        values[count].put("latitude", cursor.getInt(cursor.getColumnIndex("latitude")));
        values[count].put("longitude", cursor.getInt(cursor.getColumnIndex("longitude")));
        values[count].put("timestamp", cursor.getLong(cursor.getColumnIndex("timestamp")));
        values[count].put("network", cursor.getString(cursor.getColumnIndex("network")));
        }
        count++;
    }  while (cursor.moveToNext());
  }
cursor.close();

Upvotes: 0

Nick Wilson
Nick Wilson

Reputation: 4989

cursor.moveToFirst could be returning false. Try wrapping that in an if statement:

if (cursor.moveToFirst()) {
  do....
}

Upvotes: 0

MByD
MByD

Reputation: 137272

While I'm not sure what the problem is, you can do it in a safer way:

List<ContentValues> values = new ArrayList<ContentValues>();
if (cursor.getCount() > 0) {
    cursor.moveToFirst();
    do {
        ContentValues value = new ContentValues();
        value.put("signal_value", cursor.getInt(cursor.getColumnIndex("signal_value")));
        values.add(value);
    } while (cursor.moveToNext());
}
// ...
return values.toArray(new ContentValues[0]);

Upvotes: 0

Kevin Bowersox
Kevin Bowersox

Reputation: 94429

Your using a do...while loop, the do while loop will execute an extra iteration because the condition is checked after each iteration, not before. The code gets past your guard condition and enters the loop, which then executes 2x when there is one result.

Switch the loop to a while loop and this should work fine.

int count = 0;
    if (cursor.getCount() > 0) {
        cursor.moveToFirst();
        while (cursor.moveToNext()) {
            values[count] = new ContentValues();
            values[count].put("signal_value", cursor.getInt(cursor.getColumnIndex("signal_value")));
            values[count].put("latitude", cursor.getInt(cursor.getColumnIndex("latitude")));
            values[count].put("longitude", cursor.getInt(cursor.getColumnIndex("longitude")));
            values[count].put("timestamp", cursor.getLong(cursor.getColumnIndex("timestamp")));
            values[count].put("network", cursor.getString(cursor.getColumnIndex("network")));

            count++;

        }
    }
    cursor.close();

Upvotes: 0

Related Questions