java.util.ConcurrentModificationException в анимации Android

Есть кое-что, чего мне не хватает в понятии синхронизации кода в Android.

Сценарий

На экране всегда нарисовано 3 элемента. Каждое изображение хранится в списке ArrayList (lstGraphics). Для этой цели я использую SurfaceView. Как только пользователь нажимает на изображение, рынок изображения удаляется и добавляется новый.

Примеры кода:

АнимацияСкрытьПоток

...
    @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);
                    }
                }
            }
        }    
...

Итак, как вам может показаться, сначала я updatePhysics(). Это означает, что я рассчитываю направление, в котором будет двигаться каждое изображение. Здесь я также удалю выбранные изображения из моего списка. После этого я проверяю, нужно ли мне добавить новый элемент в мой список в manageAnimations(), а затем последний шаг рисует все это.

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

Ошибка, которую я получаю:

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

Любая помощь приветствуется. Спасибо.


person Alin    schedule 01.03.2011    source источник


Ответы (4)


Ваша проблема в вашем методе физики, где вы добавляете графику и список

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

комбинация for(Graphic graphic : lstGraphics) и lst.Graphics.remove(graphic); вызывает ConcurrentModificationException, потому что вы работаете над своим списком и одновременно пытаетесь изменить его.

Пока я знаю два решения:

  1. Вместо этого используйте итератор, если он доступен (до сих пор никогда не кодировался для Android).

    while (iter.hasNext) {
        if (physicsCondition) iter.remove();
    }
    
  2. используйте второй список для хранения элементов для удаления и удаления их впоследствии

    List<GraphicsItem> toRemove = new ....
    for (Graphic graphic : lstGraphics) {
        if (physicsCondition) {
            toRemove.add(graphic);
        }
    }
    lstGraphics.removeAll(toRemove);
    
person monty    schedule 01.03.2011
comment
Я создал List toRemove и протестировал его, он работал как шарм. Спасибо за помощь. - person Alin; 01.03.2011
comment
@ Алин, тебе следует рассмотреть решение с Iterator. Создание ненужных объектов в игровом цикле обычно плохая идея. - person denis.solonenko; 01.03.2011

Как сказал @idefix, вы можете легко получить ConcurrentModificationException в однопоточном контексте следующим образом:

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);
        }
    }
}
person denis.solonenko    schedule 01.03.2011

Вы можете использовать CopyOnWriteArrayList, как показано ниже:

    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");
        }
    }
person pqtuan86    schedule 28.06.2015

Это мой метод с использованием второго решения @idefix:

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

Спасибо @idefix +1

person Jorgesys    schedule 09.05.2014