23

There is something I miss with the notion of Synchronizing code in Android.

Scenario

There are always 3 items drawn on the screen. Each image is stored in a ArrayList (lstGraphics). For this purpose I use a SurfaceView. Once the user taps on a image, the image get's market to be removed and a new one will be added.

Code samples:

AnimationHideThread

...
    @Override
        public void run() {
            Canvas c;
            while (run) {
                c = null;
                try {
                    c = panel.getHolder().lockCanvas(null);
                      synchronized (panel.getHolder()) {

                        panel.updatePhysics();
                        panel.manageAnimations();
                        panel.onDraw(c);

                    }
                } finally {
                    if (c != null) {
                        panel.getHolder().unlockCanvasAndPost(c);
                    }
                }
            }
        }    
...

So as you can seem first I updatePhysics(). This means I calculate direction where each image will move to. In here I will also remove clicked images from my list. After that I check if I need to add a new Item in my list in manageAnimations() and then the final step draw the whole thing.

public class Panel extends SurfaceView implements SurfaceHolder.Callback {
....
 public void manageAnimations()
    {
          synchronized (this.getHolder()) {
            ...
        while (lstGraphics.size()<3) {
                lstGraphics.add(createRandomGraphic());
                }
        }
          }
    }

 @Override
    public boolean onTouchEvent(MotionEvent event) {
         synchronized (getHolder()) {
            if (event.getAction() == MotionEvent.ACTION_DOWN) {
                 //... check if a image has been clicked and then set its property
                        graphic.setTouched(true);

                 }
            }

            return true;
         }
    }

 public void updatePhysics() {
       synchronized (getHolder()) {

     for (Graphic graphic : lstGraphics) {
           //.... Do some checks
     if (graphic.isTouched())
      {
        lstGraphics.remove(graphic);
      }
     }
  }
 }

 @Override
    public void onDraw(Canvas canvas) {
         /// draw the backgrounds and each element from lstGraphics
}

public class Graphic {

        private Bitmap bitmap;
            private boolean touched;
            private Coordinates initialCoordinates; 
....
}

The error I get is:

> 03-01 10:01:53.365: ERROR/AndroidRuntime(454): Uncaught handler: thread Thread-12 exiting due to uncaught exception 
> 03-01 10:01:53.365: ERROR/AndroidRuntime(454): java.util.ConcurrentModificationException
> 03-01 10:01:53.365: ERROR/AndroidRuntime(454): at java.util.AbstractList$SimpleListIterator.next(AbstractList.java:66)
> 03-01 10:01:53.365: ERROR/AndroidRuntime(454): at com.test.customcontrols.Panel.updatePhysics(Panel.java:290)
> 03-01 10:01:53.365: ERROR/AndroidRuntime(454): at com.test.customcontrols.AnimationHideThread.run(AnimationHideThread.java:41)

Any help is greatly appreciated. Thank you.

4 Answers 4

77

Your problem is in your physics method, where you add the graphic and the list

public void updatePhysics() {
    synchronized (getHolder()) {
        for (Graphic graphic : lstGraphics) {
        //.... Do some checks
        if (graphic.isTouched()) {
            lstGraphics.remove(graphic); //your problem
        }
    }
}

the combination of for(Graphic graphic : lstGraphics) and lst.Graphics.remove(graphic); causes the ConcurrentModificationException because you are running over your list and concurrently try to modify it.

So far I know two solutions:

  1. Use an Iterator instead if one is available (never coded for Android so far).

    while (iter.hasNext) {
        if (physicsCondition) iter.remove();
    }
    
  2. use a second list to store the elements to remove and remove them afterwards

    List<GraphicsItem> toRemove = new ....
    for (Graphic graphic : lstGraphics) {
        if (physicsCondition) {
            toRemove.add(graphic);
        }
    }
    lstGraphics.removeAll(toRemove);
    
Sign up to request clarification or add additional context in comments.

2 Comments

I created List toRemove and tested it, worked like a charm. Thank you for your help.
@Alin you should consider solution with Iterator. Creating unnecessary objects in a game loop in usually a bad idea..
8

As @idefix said, you can easily get ConcurrentModificationException in single-threaded context like this:

public static void main(String[] args) {
    List<String> list = new ArrayList<String>(Arrays.asList("AAA", "BBB"));
    for (String s : list) {
        if ("BBB".equals(s)) {
            list.remove(s);
        }
    }
}

Comments

4

You can use CopyOnWriteArrayList like below:

    List<String> myList = new CopyOnWriteArrayList<String>();

    myList.add("1");
    myList.add("2");
    myList.add("3");
    myList.add("4");
    myList.add("5");

    Iterator<String> it = myList.iterator();
    while(it.hasNext()){
        String value = it.next();
        System.out.println("List Value:"+value);
        if(value.equals("3")){
            myList.remove("4");
            myList.add("6");
            myList.add("7");
        }
    }

Comments

1

This is my method using @idefix second solution:

private List<TYPE> getFilteredData(List<TYPE> data){                
    List<TYPE> toRemove = new ArrayList<TYPE>(data.size());     
    synchronized(data){
        for(TYPE f : data){
            if([CONDITION]){                        
                toRemove.add(f);
                Log.w(TAG, "Element removed: "+ f);                 
            }
        }
    }                   
    data.removeAll(toRemove);
    return data;        
}

Thanks @idefix +1

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.