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

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

сценарий

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

Образцы кода:

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

Итак, как вы можете показаться, я сначала обновляю Pysys (). Это означает, что я рассчитываю направление, в которое будут перемещаться каждое изображение. Здесь я также удаляю клики из моего списка. После этого я проверяю, нужно ли мне добавить новый элемент в свой список в 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) 

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

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

 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. Вместо этого используйте Iterator, если он доступен (он никогда не кодируется для 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); 

Как сказал @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); } } } 

Вы можете использовать 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"); } } в  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"); } } 

Это мой метод с использованием второго решения @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