java.util.ConcurrentModificationException en la animación de Android
animation (4)
Hay algo que extraño con la noción de Sincronizar el código en Android.
Guión
Siempre hay 3 elementos dibujados en la pantalla. Cada imagen se almacena en una ArrayList (lstGraphics). Para este propósito, uso SurfaceView. Una vez que el usuario toque una imagen, se eliminará el mercado de obtención de imagen y se agregará uno nuevo.
Muestras de código:
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);
}
}
}
}
...
Para que pueda parecer primero, actualizo la Física (). Esto significa que calculo la dirección hacia donde se moverá cada imagen. Aquí también eliminaré las imágenes clicadas de mi lista. Después de eso, verifico si necesito agregar un nuevo ítem en mi lista en manageAnimations () y luego el último paso dibujar todo.
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;
....
}
El error que recibo es:
> 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)
Cualquier ayuda es muy apreciada. Gracias.
Como dijo @idefix, puede obtener fácilmente ConcurrentModificationException en un contexto de subproceso único como este:
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);
}
}
}
Este es mi método usando la segunda solución de @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;
}
Gracias @idefix +1
Puede usar CopyOnWriteArrayList como a continuación:
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");
}
}
Su problema está en su método de física, donde agrega el gráfico y la lista
public void updatePhysics() {
synchronized (getHolder()) {
for (Graphic graphic : lstGraphics) {
//.... Do some checks
if (graphic.isTouched()) {
lstGraphics.remove(graphic); //your problem
}
}
}
la combinación de for(Graphic graphic : lstGraphics)
y lst.Graphics.remove(graphic);
causa la excepción ConcurrentModificationException porque está ejecutando su lista y al mismo tiempo intenta modificarla.
Hasta ahora sé dos soluciones:
Use un iterador en su lugar si uno está disponible (nunca codificado para Android hasta el momento).
while (iter.hasNext) { if (physicsCondition) iter.remove(); }
use una segunda lista para almacenar los elementos para eliminar y eliminarlos después
List<GraphicsItem> toRemove = new .... for (Graphic graphic : lstGraphics) { if (physicsCondition) { toRemove.add(graphic); } } lstGraphics.removeAll(toRemove);