Android动画中的java.util.ConcurrentModificationException

Ali*_*lin 23 animation android concurrentmodification

我在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);
                    }
                }
            }
        }    
...
Run Code Online (Sandbox Code Playgroud)

所以你可以先看看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; 
....
}
Run Code Online (Sandbox Code Playgroud)

我得到的错误是:

> 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)
Run Code Online (Sandbox Code Playgroud)

任何帮助是极大的赞赏.谢谢.

mon*_*nty 78

您的问题出在物理方法中,您可以在其中添加图形和列表

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

的组合for(Graphic graphic : lstGraphics),并lst.Graphics.remove(graphic);导致ConcurrentModificationException的,因为你是运行在你的清单,同时试图修改它.

到目前为止我知道两个解决方案

  1. 如果有可用的话,请使用Iterator(到目前为止从未为Android编码).

    while (iter.hasNext) {
        if (physicsCondition) iter.remove();
    }
    
    Run Code Online (Sandbox Code Playgroud)
  2. 使用第二个列表来存储要删除的元素,然后删除它们

    List<GraphicsItem> toRemove = new ....
    for (Graphic graphic : lstGraphics) {
        if (physicsCondition) {
            toRemove.add(graphic);
        }
    }
    lstGraphics.removeAll(toRemove);
    
    Run Code Online (Sandbox Code Playgroud)

  • @Alin你应该考虑用`Iterator`来解决.在游戏循环中创建不必要的对象通常是一个坏主意.. (5认同)

den*_*nko 8

正如@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);
        }
    }
}
Run Code Online (Sandbox Code Playgroud)