【问题标题】:java.util.ConcurrentModificationException in Android animationAndroid 动画中的 java.util.ConcurrentModificationException
【发布时间】:2011-03-01 08:20:41
【问题描述】:

我想念在 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)

非常感谢任何帮助。谢谢。

【问题讨论】:

    标签: android animation concurrentmodification


    【解决方案1】:

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

    【讨论】:

      【解决方案2】:

      您可以像下面这样使用 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");
              }
          }
      

      【讨论】:

        【解决方案3】:

        这是我使用@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

        【讨论】:

          【解决方案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);
            

          【讨论】:

          • 我创建了 List toRemove 并对其进行了测试,效果很好。感谢您的帮助。
          • @Alin 你应该考虑使用Iterator 的解决方案。在游戏循环中创建不必要的对象通常是个坏主意..
          猜你喜欢
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 2020-05-11
          • 1970-01-01
          • 1970-01-01
          • 2020-06-16
          相关资源
          最近更新 更多