Excepción de modificación simultánea: agregar a una ArrayList
El problema ocurre en
Element element = it.next();
Y este código que contiene esa línea, está dentro de unOnTouchEvent
for (Iterator<Element> it = mElements.iterator(); it.hasNext();){
Element element = it.next();
if(touchX > element.mX && touchX < element.mX + element.mBitmap.getWidth() && touchY > element.mY
&& touchY < element.mY + element.mBitmap.getHeight()) {
//irrelevant stuff..
if(element.cFlag){
mElements.add(new Element("crack",getResources(), (int)touchX,(int)touchY));
element.cFlag = false;
}
}
}
Todo esto está dentro synchronized(mElements)
, donde mElements
hay unArrayList<Element>
Cuando toco un Element
, puede que se active cFlag
, lo que creará otro Element
con propiedades diferentes, que se caerá de la pantalla y se destruirá en menos de un segundo. Es mi forma de crear efectos de partículas. Podemos llamar a esto "partícula" crack
, como el parámetro String en el constructor.
Todo esto funciona bien hasta que agrego otro main Element
. Ahora tengo dos Elements
en pantalla al mismo tiempo, y si toco el más nuevo Element
, funciona bien y lanza las partículas.
Sin embargo, si toco y activo cFlag
el anterior Element
, me da la excepción.
07-28 15:36:59.815: ERROR/AndroidRuntime(4026): FATAL EXCEPTION: main
07-28 15:36:59.815: ERROR/AndroidRuntime(4026): java.util.ConcurrentModificationException
07-28 15:36:59.815: ERROR/AndroidRuntime(4026): at java.util.ArrayList$ArrayListIterator.next(ArrayList.java:573)
07-28 15:36:59.815: ERROR/AndroidRuntime(4026): at com.Juggle2.Panel.onTouchEvent(Panel.java:823)
07-28 15:36:59.815: ERROR/AndroidRuntime(4026): at android.view.View.dispatchTouchEvent(View.java:3766)
07-28 15:36:59.815: ERROR/AndroidRuntime(4026): at android.view.ViewGroup.dispatchTouchEvent(ViewGroup.java:863)
07-28 15:36:59.815: ERROR/AndroidRuntime(4026): at android.view.ViewGroup.dispatchTouchEvent(ViewGroup.java:863)
07-28 15:36:59.815: ERROR/AndroidRuntime(4026): at com.android.internal.policy.impl.PhoneWindow$DecorView.superDispatchTouchEvent(PhoneWindow.java:1767)
07-28 15:36:59.815: ERROR/AndroidRuntime(4026): at com.android.internal.policy.impl.PhoneWindow.superDispatchTouchEvent(PhoneWindow.java:1119)
07-28 15:36:59.815: ERROR/AndroidRuntime(4026): at android.app.Activity.dispatchTouchEvent(Activity.java:2086)
07-28 15:36:59.815: ERROR/AndroidRuntime(4026): at com.android.internal.policy.impl.PhoneWindow$DecorView.dispatchTouchEvent(PhoneWindow.java:1751)
07-28 15:36:59.815: ERROR/AndroidRuntime(4026): at android.view.ViewRoot.handleMessage(ViewRoot.java:1785)
07-28 15:36:59.815: ERROR/AndroidRuntime(4026): at android.os.Handler.dispatchMessage(Handler.java:99)
07-28 15:36:59.815: ERROR/AndroidRuntime(4026): at android.os.Looper.loop(Looper.java:123)
07-28 15:36:59.815: ERROR/AndroidRuntime(4026): at android.app.ActivityThread.main(ActivityThread.java:4627)
07-28 15:36:59.815: ERROR/AndroidRuntime(4026): at java.lang.reflect.Method.invokeNative(Native Method)
07-28 15:36:59.815: ERROR/AndroidRuntime(4026): at java.lang.reflect.Method.invoke(Method.java:521)
07-28 15:36:59.815: ERROR/AndroidRuntime(4026): at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:893)
07-28 15:36:59.815: ERROR/AndroidRuntime(4026): at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:651)
07-28 15:36:59.815: ERROR/AndroidRuntime(4026): at dalvik.system.NativeStart.main(Native Method)
¿Cómo puedo hacer que esto funcione?
ConcurrentModificationException ocurre cuando modifica la lista (agregando o eliminando elementos) mientras recorre una lista con Iterator
.
Intentar
List<Element> thingsToBeAdd = new ArrayList<Element>();
for(Iterator<Element> it = mElements.iterator(); it.hasNext();) {
Element element = it.next();
if(...) {
//irrelevant stuff..
if(element.cFlag){
// mElements.add(new Element("crack",getResources(), (int)touchX,(int)touchY));
thingsToBeAdd.add(new Element("crack",getResources(), (int)touchX,(int)touchY));
element.cFlag = false;
}
}
}
mElements.addAll(thingsToBeAdd );
También deberías considerar mejorar cada bucle como sugirió Jon.
Normalmente uso algo como esto:
for (Element element : new ArrayList<Element>(mElements)) {
...
}
rápido, limpio y sin errores
otra opción es usar CopyOnWriteArrayList
No puedes agregar una entrada a una colección mientras estás iterando sobre ella.
Una opción es crear una nueva List<Element>
para las nuevas entradas mientras estás iterando mElements
y luego agregar todas las nuevas mElement
después ( mElements.addAll(newElements)
). Por supuesto, eso significa que no habrás ejecutado el cuerpo del bucle para esos nuevos elementos. ¿Es eso un problema?
Al mismo tiempo, te recomiendo que actualices tu código para usar el bucle for mejorado :
for (Element element : mElements) {
...
}