java - Excepción de modificación concurrente: agregar a una ArrayList
android exception (7)
Bueno, he intentado todos los aspectos en mi caso donde estaba iterando en una lista de adaptadores, pero debido a los golpes una y otra vez, me mostró el mensaje de excepción que se lanza. Intenté lanzar la lista a
= (CopyOnWriteArraylist<MyClass>)mylist.value;
pero también me arrojó una excepción de CouldNotCastException, (y finalmente reflexioné sobre el hecho de que por qué usan o nos proporcionan una fachada de casting).
Incluso utilicé el llamado Bloque Sincronizado también, pero incluso no funcionó o podría haberlo usado de forma incorrecta.
Por lo tanto, es todo cuando finalmente utilicé la # técnica de #todo de tiempo # para manejar la excepción en el bloque try catch, y funcionó. Así que pon tu código en el
try{
//block
}catch(ConcurrentModificationException){
//thus handling my code over here
}
El problema ocurre en
Element element = it.next();
Y este código que contiene esa línea, está dentro de un OnTouchEvent
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
es un ArrayList<Element>
Cuando toco un Element
, puede activar cFlag
, que creará otro Element
con diferentes propiedades, que se caerá de la pantalla y se destruirá a sí mismo en menos de un segundo. Es mi forma de crear efectos de partículas. Podemos llamar a esta crack
"partículas", como el parámetro String en el constructor.
Todo esto funciona bien hasta que agregue otro Element
principal. Ahora tengo dos Elements
en la pantalla al mismo tiempo, y si toco el Element
más nuevo, funciona bien y lanza las partículas.
Sin embargo, si toco y activo cFlag
en el Element
anterior, entonces 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?
El uso de Iterators también soluciona problemas de concurrencia, como este:
Iterator<Object> it = iterator.next().iterator();
while (it.hasNext()) {
it.remove();
}
No puede agregar una entrada a una colección mientras la itera.
Una opción es crear un nuevo List<Element>
para nuevas entradas mientras itera sobre mElements
, y luego agrega todas las nuevas a mElement
después ( mElements.addAll(newElements)
). Por supuesto, eso significa que no habrá ejecutado el cuerpo del bucle para esos nuevos elementos, ¿es eso un problema?
Al mismo tiempo, recomiendo que actualice su código para usar el bucle for mejorado :
for (Element element : mElements) {
...
}
Normalmente uso algo como esto:
for (Element element : new ArrayList<Element>(mElements)) {
...
}
rápido, limpio y libre de errores
otra opción es usar CopyOnWriteArrayList
Un bucle indexado también debería funcionar.
for (int i = 0; i < collection.size(); i++)
agregar de la lista en este caso conduce a CME, ninguna cantidad de synchronized
le permitirá evitar eso. En su lugar, considere agregar usando el iterador ...
for(ListIterator<Element> it = mElements.listIterator(); 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));
it.add(new Element("crack",getResources(), (int)touchX,(int)touchY));
element.cFlag = false;
}
}
}
También creo que es algo resbaladizo decir como ...
... El problema ocurre en
Element element = it.next();
por el bien de la precisión, tenga en cuenta que lo anterior no está garantizado.
ConcurrentModificationException señala que este ... comportamiento no se puede garantizar ya que, en términos generales, es imposible hacer ninguna garantía dura en presencia de modificaciones concurrentes no sincronizadas. Las operaciones de falla rápida lanzan ConcurrentModificationException sobre una base de mejor esfuerzo ...
ConcurrentModificationException produce cuando modifica la lista (agregando o eliminando elementos) al atravesar una lista con Iterator
.
Tratar
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ía considerar mejorar para cada ciclo como Jon sugirió.