Le problème se pose à
Element element = it.next();
Et ce code qui contient cette ligne est à l'intérieur d'une 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;
}
}
}
Tout cela se trouve dans synchronized(mElements)
, où mElements
est un ArrayList<Element>
Lorsque je touche une Element
, il peut activer cFlag
, ce qui créera une autre Element
avec différentes propriétés, qui tombera de l'écran et se détruira en moins d'une seconde. C'est ma façon de créer des effets de particules. Nous pouvons appeler cette "particule" crack
, comme le paramètre String du constructeur.
Tout cela fonctionne bien jusqu'à ce que j'ajoute une autre variable principale Element
. Maintenant, j'ai deux Elements
à l'écran en même temps, et si je touche la dernière Element
, cela fonctionne bien et lance les particules.
Cependant, si je touche et active cFlag
sur la plus ancienne Element
, cela me donne l'exception.
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)
Comment puis-je faire ce travail?
ConcurrentModificationException se produit lorsque vous modifiez la liste (en ajoutant ou en supprimant des éléments) tout en parcourant une liste avec Iterator
.
Essayer
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 );
Aussi, vous devriez envisager une amélioration pour chaque boucle, comme suggéré par Jon.
J'utilise normalement quelque chose comme ça:
for (Element element : new ArrayList<Element>(mElements)) {
...
}
rapide, propre et sans bug
une autre option consiste à utiliser CopyOnWriteArrayList
Vous n'êtes pas autorisé à ajouter une entrée à une collection lorsque vous le parcourez.
Une option consiste à créer un nouveau List<Element>
pour les nouvelles entrées pendant une itération sur mElements
, puis à ajouter toutes les nouvelles entrées à mElement
par la suite (mElements.addAll(newElements)
). Bien sûr, cela signifie que vous n'avez pas exécuté le corps de la boucle pour ces nouveaux éléments - est-ce un problème?
Dans le même temps, je vous recommande de mettre à jour votre code pour utiliser le amélioré pour la boucle :
for (Element element : mElements) {
...
}
Une boucle indexée pour devrait également fonctionner.
for (int i = 0; i < collection.size(); i++)
ajouter de la liste dans ce cas conduit à CME, aucune quantité de synchronized
ne vous permettra d'éviter cela. Au lieu de cela, pensez à ajouter en utilisant l'itérateur ...
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;
}
}
}
Aussi, je pense que c'est un peu glissant de dire comme ...
... Le problème se produit à
Element element = it.next();
par souci de précision, notez que ce qui précède n’est pas garanti.
Documentation de l'API souligne que ce comportement ...) ne peut pas être garanti, car il est généralement impossible de donner des garanties strictes en présence de modifications simultanées non synchronisées. Les opérations de défaillance rapide lancent ConcurrentModificationException sur un base du meilleur effort ...
L'utilisation d'Iterators corrige également les problèmes de concurrence, tels que:
Iterator<Object> it = iterator.next().iterator();
while (it.hasNext()) {
it.remove();
}
Eh bien, j’ai essayé tous les aspects de mon cas où j’étais en train d’itérer dans un adaptateur une liste, mais en raison de frapper encore et encore, je m’ai montré le message de l’exception qui avait été lancée.
= (CopyOnWriteArraylist<MyClass>)mylist.value;
mais il m'a aussi jeté une exception de CouldNotCastException (et j'ai finalement réfléchi au fait que pourquoi utilisent-ils ou nous fournissent-ils une facalité de casting).
J'ai même utilisé le soi-disant bloc synchronisé aussi, mais cela n'a pas fonctionné ou je l'aurais peut-être mal utilisé.
Ainsi, c’est tout quand j’ai finalement utilisé le #all of time # Technique de gestion de l’exceptionin catch catch block, et cela a fonctionnéAlors placez votre code dans le
try{
//block
}catch(ConcurrentModificationException){
//thus handling my code over here
}