2011-03-01 74 views
22

我錯過了Android中同步代碼的概念。Android動畫中的java.util.ConcurrentModificationException

方案

總有一些在屏幕上繪製的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); 
        } 
       } 
      } 
     }  
... 

所以你可以先看起來我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) 

任何幫助是極大的讚賞。謝謝。

回答

77

你的問題是你的物理方法,在這裏你,因爲你正在運行在你的列表中添加圖形和列表

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); 
    
+1

我創建的列表的文檔,刪除和測試它,工作就像一個魅力。感謝您的幫助。 – Alin 2011-03-01 09:04:54

+5

@Alin你應該考慮'Iterator'的解決方案。在遊戲循環中創建不必要的對象通常是一個壞主意。 – 2011-03-01 11:07:49

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

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

3

可以使用的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"); 
     } 
    }