Search code examples
androidconcurrencyandroid-canvasconcurrentmodification

Android ConcurrentModification onDraw()


I'm having an issue with an android application.

I have a thread that constantly iterators over a list or shapes, updates their positions, and sometimes removes an item from the list. At the end of the thread's while loop it calls postInvalidate() to prompt a repaint.

Here is the code where modification to the ArrayList happens.

Iterator<Shape> iterator = this.myList.iterator();
while (iterator.hasNext()) {
     Shape nextShape = iterator.next();
     nextShape.move();
     if(condition) {
         iterator.remove()
     }
}

The onDraw method uses a for each loop to draw each of those items. I am getting concurrent modification in the onDraw method despite only modifying the list through the iterator. I've tried CopyOnWriteArrayList and Collections.synchronized with the same results.

Any help would be appreciated.


Solution

  • There's a couple of ways to solve this. One would be to use a synchronized block in the thread and in onDraw, but that would prevent the 2nd thread from running at the same time as the UI thread.

    I think the best way is for you to use two collections. Your code should look like this

    onDraw(){
        synchronized(myList){
            //Do your draw code.  Get the iterator inside this block
        }
    }
    
    threadFunction(){
        List threadList;
        while(1){
            //Update threadList, which holds all the objects
            synchronized(myList){
                myList = threadList.copy();  //Do a deep copy, don't just copy over the reference
            }
            postInvalidate();
        }
    }
    

    This will make your draw function operate on a copy of the list made at the end of an iteration. If you're working on a deep copy, you won't get any synchronization problems as the thread isn't altering the same list. The synchronized blocks will stop the thread from overwriting the draw function's copy during a draw. The only remaining issue is that the overwrite of the list in the thread will hang until a draw is done, but since updating it more frequently than that won't show on screen anyway, my guess is that's acceptable.