Search code examples
javaandroidconcurrentmodification

java.util.ConcurrentModificationException only after multiple touch events


I get a java.util.ConcurrentModificationException in my application, but only if I perform several touch events in rapid sucession, and seems to have more of an effect on slower devices (Samsung Galaxy S) as opposed to faster ones (Galaxy S3).

I have a queue where I store an array of int, to store the opacity and data for the points of the corresponding line. I remove the line from the queue when the opacity reaches 0, so it is no longer drawn.

Here's the stack trace:

01-10 14:06:27.847: E/AndroidRuntime(2081): java.util.ConcurrentModificationException
01-10 14:06:27.847: E/AndroidRuntime(2081):     at java.util.LinkedList$LinkIterator.next(LinkedList.java:124)
01-10 14:06:27.847: E/AndroidRuntime(2081):     at com.android.mcameron.singletrack.Options$TestSurfaceView.drawLinesInvalid(Options.java:559)
01-10 14:06:27.847: E/AndroidRuntime(2081):     at com.android.mcameron.singletrack.Options$TestSurfaceView.run(Options.java:438)
01-10 14:06:27.847: E/AndroidRuntime(2081):     at java.lang.Thread.run(Thread.java:1019)

These are the 2 functions that loop through the Queue and I perform my remove operation.

    private void drawLinesInvalid() {
        paint.setColor(Color.RED);

        for (Iterator<int[]> iterator = invalidLines.iterator(); iterator.hasNext();) {
            int[] invalidLine = iterator.next(); // Line 559 with the error
            float[] line = {invalidLine[1], invalidLine[2], invalidLine[3], invalidLine[4]};

            Xfermode originalXfermode = paint.getXfermode();
            paint.setXfermode(new PorterDuffXfermode(PorterDuff.Mode.CLEAR));
            mCanvas.drawLines(line, paint);
            paint.setXfermode(originalXfermode);
            paint.setAlpha(invalidLine[0]);
            mCanvas.drawLines(line, paint);
        }
    }

    private void decreaseLineOpacity() {
        for (Iterator<int[]> iterator = invalidLines.iterator(); iterator.hasNext();) {
            int[] line = iterator.next();
            if (line[0] <= 0) {
                iterator.remove();
            }
        }

        for (Iterator<int[]> iterator = invalidLines.iterator(); iterator.hasNext();) {
            int[] line = iterator.next();
            line[0] -= 10;
        }
    }

I've looked at a fair few other questions here, and most people seem to have problems with using Foreach loops, and performing the remove() operation on the queue rather then the iterator. Both of which I believe don't apply here. Also like said above I can get away with touching, so it's not an error that happens straight away. I can sometimes touch away 30-50 imes before it throws the exception.


Solution

  • Thanks to Peter Lawrey, I used a CopyOnWriteArrayList

    I changed my queue to this:

    private CopyOnWriteArrayList<int[]> invalidLines = new CopyOnWriteArrayList<int[]>();
    

    and then updated my decreaseLineOpacity to this:

    private void decreaseLineOpacity() {
        int position = 0;
        for (Iterator<int[]> iterator = invalidLines.iterator(); iterator.hasNext();) {
            int[] line = iterator.next();
            if (line[0] <= 0) {
                invalidLines.remove(position);
            }
            position++;
        }
    
        for (Iterator<int[]> iterator = invalidLines.iterator(); iterator.hasNext();) {
            int[] line = iterator.next();
            line[0] -= 10;
            Log.d("Counting", "Opacity: "+ line[0]);
        }
    }
    

    I've been clicking away for a couple minutes and it hasn't thrown an exception, so seems good