简体   繁体   中英

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

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()) .

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.

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();

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();

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]);

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

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

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM