java.util.ConcurrentModificationException – ArrayList

START EDIT , desplácese hacia abajo para ver el código actualizado END OF EDIT

He google y buscado alrededor de SO por qué esta excepción está ocurriendo y entiendo que es causado por un objeto está leyendo una lista y mientras tanto un elemento fue eliminado de la lista.

He cambiado mi código de acuerdo a las sugerencias que he encontrado, pero de vez en cuando todavía recibo esta excepción y se está estrellando mi aplicación. Y parece al azar, trato de replicar la excepción y el 90% del tiempo no recibo la excepción y no siempre siguiendo el mismo procedimiento, lo que hace que sea difícil de depurar.

Estoy usando el patrón del observador. A veces sucede con el método unregister , otras veces con el register , otras veces con un método de la notify … es bastante al azar a donde sucede.

Estoy usando un android asynctask para descargar algunos bytes de mi servidor y el patrón de observador es actualizar la GUI cuando sea necesario.

Aquí está mi código:

 @Override public void register(final Observer newObserver) { Log.d(TAG, "(Register) Observer registred: " + newObserver.toString()); observers.add(newObserver); Log.d(TAG, "(Register) Number of registered observers: " + observers.size()); } @Override public void unregister(final Observer observer) { int indexObersver = observers.indexOf(observer); // Avoid java.util.ConcurrentModificationException // at java.util.ArrayList$ArrayListIterator.next(ArrayList.java) if(indexObersver >= 0) { observers.remove(indexObersver); Log.d(TAG, "(Unregister) Unregistered Observer: " + observer.toString()); Log.d(TAG, "(Unregister) Now we have: " + observers.size() + " observers"); } else { Log.d(TAG, "(Unregister) Registered Observer not found"); } } @Override public void notifyObserverNewLocalBackup(BackupInfo backupInfo) { // Avoid java.util.ConcurrentModificationException // at java.util.ArrayList$ArrayListIterator.next(ArrayList.java) for( Iterator< Observer > it = observers.iterator(); it.hasNext() ; ) // for(Observer observer : observers) { Observer observer = it.next(); observer.notifyNewLocalBackup(backupInfo); } } @Override public void notifyObserverNewRemoteBackup(ArrayList<PhoneBackup> phoneBackups) { // Avoid java.util.ConcurrentModificationException // at java.util.ArrayList$ArrayListIterator.next(ArrayList.java) // for(Observer observer : observers) for( Iterator< Observer > it = observers.iterator(); it.hasNext() ; ) { Observer observer = it.next(); observer.notifyNewRemoteBackup(phoneBackups); } } @Override public void notifyObserverDownloadCompleted(PhoneBackup phoneBackup) { // Avoid java.util.ConcurrentModificationException // at java.util.ArrayList$ArrayListIterator.next(ArrayList.java) // for(Observer observer : observers) for( Iterator< Observer > it = observers.iterator(); it.hasNext() ; ) { Observer observer = it.next(); observer.notifyDownloadCompleted(phoneBackup); } } @Override public void notifyObserverUploadCompleted(boolean isSucccess) { // Avoid java.util.ConcurrentModificationException // at java.util.ArrayList$ArrayListIterator.next(ArrayList.java) // for(Observer observer : observers) for( Iterator< Observer > it = observers.iterator(); it.hasNext() ; ) { Observer observer = it.next(); observer.notifyUploadCompleteted(isSucccess); } } 

Ahora la última vez que recibí la excption que sucedió en notifyObserverNewRemoteBackup método en la línea Observer observer = it.next();

 06-12 04:31:58.394: W/dalvikvm(31358): threadid=1: thread exiting with uncaught exception (group=0x418fcce0) 06-12 04:31:58.629: E/AndroidRuntime(31358): FATAL EXCEPTION: main 06-12 04:31:58.629: E/AndroidRuntime(31358): Process: com.mypackage.android.design.appdesgin, PID: 31358 06-12 04:31:58.629: E/AndroidRuntime(31358): java.util.ConcurrentModificationException 06-12 04:31:58.629: E/AndroidRuntime(31358): at java.util.ArrayList$ArrayListIterator.next(ArrayList.java:573) 06-12 04:31:58.629: E/AndroidRuntime(31358): at com.mypackage.android.design.appdesgin.asynctasks.ObserverSubjectManager.notifyObserverNewRemoteBackup(ObserverSubjectManager.java:99) 06-12 04:31:58.629: E/AndroidRuntime(31358): at com.mypackage.android.design.appdesgin.asynctasks.BackupsHandler$1.success(BackupsHandler.java:318) 06-12 04:31:58.629: E/AndroidRuntime(31358): at com.mypackage.android.design.appdesgin.asynctasks.BackupsHandler$1.success(BackupsHandler.java:1) 06-12 04:31:58.629: E/AndroidRuntime(31358): at retrofit.CallbackRunnable$1.run(CallbackRunnable.java:45) 06-12 04:31:58.629: E/AndroidRuntime(31358): at android.os.Handler.handleCallback(Handler.java:733) 06-12 04:31:58.629: E/AndroidRuntime(31358): at android.os.Handler.dispatchMessage(Handler.java:95) 06-12 04:31:58.629: E/AndroidRuntime(31358): at android.os.Looper.loop(Looper.java:136) 06-12 04:31:58.629: E/AndroidRuntime(31358): at android.app.ActivityThread.main(ActivityThread.java:5081) 06-12 04:31:58.629: E/AndroidRuntime(31358): at java.lang.reflect.Method.invokeNative(Native Method) 06-12 04:31:58.629: E/AndroidRuntime(31358): at java.lang.reflect.Method.invoke(Method.java:515) 06-12 04:31:58.629: E/AndroidRuntime(31358): at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:791) 06-12 04:31:58.629: E/AndroidRuntime(31358): at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:607) 06-12 04:31:58.629: E/AndroidRuntime(31358): at dalvik.system.NativeStart.main(Native Method) 

———————- EDIT ————————— —-

He seguido Anubian Noob sugerencia y he implementado una lista sincronizada, pero todavía estoy recibiendo la excepción.

Aquí está mi código actualizado:

 // Singleton public synchronized static ObserverSubjectManager getInstance() { if(instance == null) { instance = new ObserverSubjectManager(); return instance; } return instance; } private ObserverSubjectManager() { // observers = new ArrayList<>(); observers = Collections.synchronizedList(new ArrayList<Observer>()); } @Override public void register(final Observer newObserver) { Log.d(TAG, "(Register) Observer registred: " + newObserver.toString()); synchronized (observers) { observers.add(newObserver); } Log.d(TAG, "(Register) Number of registered observers: " + observers.size()); } @Override public void unregister(final Observer observer) { synchronized (observers) { int indexObersver = observers.indexOf(observer); if(indexObersver >= 0) { observers.remove(indexObersver); Log.d(TAG, "(Unregister) Unregistered Observer: " + observer.toString()); Log.d(TAG, "(Unregister) Now we have: " + observers.size() + " observers"); } else { Log.d(TAG, "(Unregister) Registered Observer not found"); } } } @Override public void notifyObserverNewLocalBackup(final BackupInfo backupInfo) { synchronized (observers) { for(Observer observer : observers) { observer.notifyNewLocalBackup(backupInfo); } } } @Override public void notifyObserverNewRemoteBackup(final ArrayList<PhoneBackup> phoneBackups) { synchronized (observers) { for(Observer observer : observers) { observer.notifyNewRemoteBackup(phoneBackups); } } } @Override public void notifyObserverDownloadCompleted(final PhoneBackup phoneBackup) { synchronized (observers) { for(Observer observer : observers) { observer.notifyDownloadCompleted(phoneBackup); } } } @Override public void notifyObserverUploadCompleted(final boolean isSucccess) { synchronized (observers) { for(Observer observer : observers) { observer.notifyUploadCompleteted(isSucccess); } } } 

Stacktrace:

 06-12 05:12:49.359: W/dalvikvm(31735): threadid=1: thread exiting with uncaught exception (group=0x418fcce0) 06-12 05:12:49.426: E/AndroidRuntime(31735): FATAL EXCEPTION: main 06-12 05:12:49.426: E/AndroidRuntime(31735): Process: com.mypackage.android.design.appdesgin, PID: 31735 06-12 05:12:49.426: E/AndroidRuntime(31735): java.util.ConcurrentModificationException 06-12 05:12:49.426: E/AndroidRuntime(31735): at java.util.ArrayList$ArrayListIterator.next(ArrayList.java:573) 06-12 05:12:49.426: E/AndroidRuntime(31735): at com.mypackage.android.design.appdesgin.asynctasks.ObserverSubjectManager.notifyObserverDownloadCompleted(ObserverSubjectManager.java:126) 06-12 05:12:49.426: E/AndroidRuntime(31735): at com.mypackage.android.design.appdesgin.asynctasks.BackupsHandler$2.success(BackupsHandler.java:336) 06-12 05:12:49.426: E/AndroidRuntime(31735): at com.mypackage.android.design.appdesgin.asynctasks.BackupsHandler$2.success(BackupsHandler.java:1) 06-12 05:12:49.426: E/AndroidRuntime(31735): at retrofit.CallbackRunnable$1.run(CallbackRunnable.java:45) 06-12 05:12:49.426: E/AndroidRuntime(31735): at android.os.Handler.handleCallback(Handler.java:733) 06-12 05:12:49.426: E/AndroidRuntime(31735): at android.os.Handler.dispatchMessage(Handler.java:95) 06-12 05:12:49.426: E/AndroidRuntime(31735): at android.os.Looper.loop(Looper.java:136) 06-12 05:12:49.426: E/AndroidRuntime(31735): at android.app.ActivityThread.main(ActivityThread.java:5081) 06-12 05:12:49.426: E/AndroidRuntime(31735): at java.lang.reflect.Method.invokeNative(Native Method) 06-12 05:12:49.426: E/AndroidRuntime(31735): at java.lang.reflect.Method.invoke(Method.java:515) 06-12 05:12:49.426: E/AndroidRuntime(31735): at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:791) 06-12 05:12:49.426: E/AndroidRuntime(31735): at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:607) 06-12 05:12:49.426: E/AndroidRuntime(31735): at dalvik.system.NativeStart.main(Native Method) 

Para seguir el comentario @ Rogue, buscaría cualquier instancia donde cualquiera de sus notifyDownloadCompleted() notify notifyDownloadCompleted() , etc.) notifyDownloadCompleted() registro de un observador. Lo que puede suceder fácilmente es que:

1) Estás repitiendo una colección. Mientras que en esa iteración, usted llama a un método en uno de los observadores registrados.

2) Ese observador registrado, en la devolución de llamada de notify , llama a través de desregistrarse de otras notificaciones.

3) Puesto que todavía estás en ese bucle de iteración, esto causará una ConcurrentModificationException ya que no puedes modificar una colección mientras iteras sobre ella.

Usted podría solucionar esto haciendo un bucle inverso:

 for (int i = collection.size() - 1; i >= 0; i--) { collection.get(i).notifyDownloadCompleted(); } 

A pesar de que técnicamente todavía se puede ejecutar en algunos casos de borde, pero no una excepción.

El problema es que estás accediendo a tu ArrayList desde otro hilo, lo que significa que cuando lo modificas obtienes esa excepción. Una solución fácil es reemplazar su ArrayList con un CopyOnWriteArrayList (que es mucho más lento), o utilizar Collections.synchronizedList() .

Para hacer una lista sincronizada:

 List<Observer> list = Collection.synchronizedList(new ArrayList<Observer>); 

Si no está accediendo a la colección desde varios subprocesos, sino sólo desea evitar problemas al cambiar la colección mientras itera sobre ella, probablemente la forma más fácil es iterar sobre una copia de su colección:

 for (Observer observer : new ArrayList<>(observers)) { observer.notifyNewLocalBackup(backupInfo); } 

Esto implica una cierta sobrecarga para crear la copia, por supuesto.

También puede utilizar un CopyOnWriteArrayList , que cubre el caso de acceso de subprocesos simultáneos, también.

  • La fuente personalizada da un glifo incorrecto para ciertas combinaciones de letras en Android
  • ¿Cómo puedo convertir una parte del archivo de código fuente de Java a Kotlin?
  • Android: la adición de vistas a las imágenes no se actualiza - onClick
  • Cómo probar la actualización de la base de datos sqlite antes de cargar la nueva versión de mi aplicación en la tienda de juegos en android
  • HTTPS con resultado de autenticación básico en Unauthorized
  • GSON lanzando "BEGIN_OBJECT esperado pero fue BEGIN_ARRAY"?
  • Adaptador de ListView personalizado lanza UnsupportedOperationException
  • ¿Cómo obtener las unidades preferidas por el usuario? (Métrico (metros) o imperial (millas))?
  • Los fragmentos de Android deben estar estáticos
  • ¿Por qué añadir <merge> cambia el diseño de Android?
  • Facebook SDK v3.5.2 - Solicitar permisos desde una sesión cerrada
  • FlipAndroid es un fan de Google para Android, Todo sobre Android Phones, Android Wear, Android Dev y Aplicaciones para Android Aplicaciones.